From 4c2e5a3e01cd04b1284590740e6a0ac5e7b5ec65 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Fri, 12 Apr 2024 17:21:41 -0500 Subject: [PATCH 1/5] feat: Lazy load `Parse.CoreManager` controllers --- README.md | 13 +++ integration/test/ParseReactNativeTest.js | 83 ++++++++++++++++++++ src/LiveQueryClient.js | 14 ---- src/LocalDatastoreController.default.js | 67 ++++++++++++++++ src/LocalDatastoreController.js | 72 ++--------------- src/Parse.ts | 8 +- src/Storage.js | 10 --- src/StorageController.js | 9 +++ src/WebSocketController.js | 18 +++++ src/__tests__/InstallationController-test.js | 2 + src/__tests__/LiveQueryClient-test.js | 3 + src/__tests__/ParseConfig-test.js | 2 + src/__tests__/ParseUser-test.js | 2 + src/__tests__/browser-test.js | 5 +- src/__tests__/react-native-test.js | 12 ++- src/__tests__/weapp-test.js | 8 +- 16 files changed, 230 insertions(+), 98 deletions(-) create mode 100644 integration/test/ParseReactNativeTest.js create mode 100644 src/LocalDatastoreController.default.js create mode 100644 src/StorageController.js create mode 100644 src/WebSocketController.js diff --git a/README.md b/README.md index ae223379f..5111d1d52 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ A library that gives you access to the powerful Parse Server backend from your J - [Getting Started](#getting-started) - [Using Parse on Different Platforms](#using-parse-on-different-platforms) + - [Core Manager](#core-manager) - [Compatibility](#compatibility) - [Parse Server](#parse-server) - [Node.js](#nodejs) @@ -89,6 +90,18 @@ $ npm install @types/parse Types are updated manually after every release. If a definition doesn't exist, please submit a pull request to [@types/parse][types-parse] +#### Core Manager + +The SDK has a [Core Manager](src/CoreManager.js) that handles all configurations and controllers. These modules can be swapped out for customization before you initialize the SDK. For full list of all available modules take a look at the [Core Manager Documentation](src/CoreManager.js). + +```js +// Configuration example +Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 1) + +// Controller example +Parse.CoreManager.setRESTController(MyRESTController); +``` + ## Compatibility ### Parse Server diff --git a/integration/test/ParseReactNativeTest.js b/integration/test/ParseReactNativeTest.js new file mode 100644 index 000000000..7c9140479 --- /dev/null +++ b/integration/test/ParseReactNativeTest.js @@ -0,0 +1,83 @@ +'use strict'; + +const Parse = require('../../react-native'); +const CryptoController = require('../../lib/react-native/CryptoController'); +const LocalDatastoreController = require('../../lib/react-native/LocalDatastoreController.default'); +const StorageController = require('../../lib/react-native/StorageController.default'); +const RESTController = require('../../lib/react-native/RESTController'); + +RESTController._setXHR(require('xmlhttprequest').XMLHttpRequest); + +describe('Parse React Native', () => { + beforeEach(() => { + // Set up missing controllers and configurations + Parse.CoreManager.setWebSocketController(require('ws')); + Parse.CoreManager.setEventEmitter(require('events').EventEmitter); + Parse.CoreManager.setLocalDatastoreController(LocalDatastoreController); + Parse.CoreManager.setStorageController(StorageController); + Parse.CoreManager.setRESTController(RESTController); + Parse.CoreManager.setCryptoController(CryptoController); + + Parse.initialize('integration'); + Parse.CoreManager.set('SERVER_URL', 'http://localhost:1337/parse'); + Parse.CoreManager.set('MASTER_KEY', 'notsosecret'); + Parse.enableLocalDatastore(); + }); + + afterEach(async () => { + await Parse.User.logOut(); + Parse.Storage._clear(); + }); + + it('can log in a user', async () => { + // Handle Storage Controller + await Parse.User.signUp('asdf', 'zxcv') + const user = await Parse.User.logIn('asdf', 'zxcv'); + expect(user.get('username')).toBe('asdf'); + expect(user.existed()).toBe(true); + }); + + it('can encrypt user', async () => { + // Handle Crypto Controller + Parse.User.enableUnsafeCurrentUser(); + Parse.enableEncryptedUser(); + Parse.secret = 'My Secret Key'; + const user = new Parse.User(); + user.setUsername('usernameENC'); + user.setPassword('passwordENC'); + await user.signUp(); + + const path = Parse.Storage.generatePath('currentUser'); + const encryptedUser = Parse.Storage.getItem(path); + + const crypto = Parse.CoreManager.getCryptoController(); + + const decryptedUser = crypto.decrypt(encryptedUser, Parse.CoreManager.get('ENCRYPTED_KEY')); + expect(JSON.parse(decryptedUser).objectId).toBe(user.id); + + const currentUser = Parse.User.current(); + expect(currentUser).toEqual(user); + + const currentUserAsync = await Parse.User.currentAsync(); + expect(currentUserAsync).toEqual(user); + await Parse.User.logOut(); + Parse.CoreManager.set('ENCRYPTED_USER', false); + Parse.CoreManager.set('ENCRYPTED_KEY', null); + }); + + it('can pin saved object LDS', async () => { + // Handle LocalDatastore Controller + function LDS_KEY(object) { + return Parse.LocalDatastore.getKeyForObject(object); + } + const object = new Parse.Object('TestObject'); + object.set('field', 'test'); + await object.save(); + await object.pin(); + const localDatastore = await Parse.LocalDatastore._getAllContents(); + const cachedObject = localDatastore[LDS_KEY(object)][0]; + expect(Object.keys(localDatastore).length).toBe(2); + expect(cachedObject.objectId).toBe(object.id); + expect(cachedObject.field).toBe('test'); + }); +}); diff --git a/src/LiveQueryClient.js b/src/LiveQueryClient.js index f7d3e5318..b66a353fa 100644 --- a/src/LiveQueryClient.js +++ b/src/LiveQueryClient.js @@ -1,5 +1,3 @@ -/* global WebSocket */ - import CoreManager from './CoreManager'; import ParseObject from './ParseObject'; import LiveQuerySubscription from './LiveQuerySubscription'; @@ -502,16 +500,4 @@ class LiveQueryClient { } } -if (process.env.PARSE_BUILD === 'node') { - CoreManager.setWebSocketController(require('ws')); -} else if (process.env.PARSE_BUILD === 'browser') { - CoreManager.setWebSocketController( - typeof WebSocket === 'function' || typeof WebSocket === 'object' ? WebSocket : null - ); -} else if (process.env.PARSE_BUILD === 'weapp') { - CoreManager.setWebSocketController(require('./Socket.weapp')); -} else if (process.env.PARSE_BUILD === 'react-native') { - CoreManager.setWebSocketController(WebSocket); -} - export default LiveQueryClient; diff --git a/src/LocalDatastoreController.default.js b/src/LocalDatastoreController.default.js new file mode 100644 index 000000000..7116f28fd --- /dev/null +++ b/src/LocalDatastoreController.default.js @@ -0,0 +1,67 @@ +/** + * @flow + */ +import { isLocalDatastoreKey } from './LocalDatastoreUtils'; +import Storage from './Storage'; + +const LocalDatastoreController = { + async fromPinWithName(name: string): Array { + const values = await Storage.getItemAsync(name); + if (!values) { + return []; + } + const objects = JSON.parse(values); + return objects; + }, + + pinWithName(name: string, value: any) { + const values = JSON.stringify(value); + return Storage.setItemAsync(name, values); + }, + + unPinWithName(name: string) { + return Storage.removeItemAsync(name); + }, + + async getAllContents(): Object { + const keys = await Storage.getAllKeysAsync(); + return keys.reduce(async (previousPromise, key) => { + const LDS = await previousPromise; + if (isLocalDatastoreKey(key)) { + const value = await Storage.getItemAsync(key); + try { + LDS[key] = JSON.parse(value); + } catch (error) { + console.error('Error getAllContents: ', error); + } + } + return LDS; + }, Promise.resolve({})); + }, + + // Used for testing + async getRawStorage(): Object { + const keys = await Storage.getAllKeysAsync(); + return keys.reduce(async (previousPromise, key) => { + const LDS = await previousPromise; + const value = await Storage.getItemAsync(key); + LDS[key] = value; + return LDS; + }, Promise.resolve({})); + }, + + async clear(): Promise { + const keys = await Storage.getAllKeysAsync(); + + const toRemove = []; + for (const key of keys) { + if (isLocalDatastoreKey(key)) { + toRemove.push(key); + } + } + const promises = toRemove.map(this.unPinWithName); + return Promise.all(promises); + }, +}; + +module.exports = LocalDatastoreController; diff --git a/src/LocalDatastoreController.js b/src/LocalDatastoreController.js index 7116f28fd..df2e666d9 100644 --- a/src/LocalDatastoreController.js +++ b/src/LocalDatastoreController.js @@ -1,67 +1,5 @@ -/** - * @flow - */ -import { isLocalDatastoreKey } from './LocalDatastoreUtils'; -import Storage from './Storage'; - -const LocalDatastoreController = { - async fromPinWithName(name: string): Array { - const values = await Storage.getItemAsync(name); - if (!values) { - return []; - } - const objects = JSON.parse(values); - return objects; - }, - - pinWithName(name: string, value: any) { - const values = JSON.stringify(value); - return Storage.setItemAsync(name, values); - }, - - unPinWithName(name: string) { - return Storage.removeItemAsync(name); - }, - - async getAllContents(): Object { - const keys = await Storage.getAllKeysAsync(); - return keys.reduce(async (previousPromise, key) => { - const LDS = await previousPromise; - if (isLocalDatastoreKey(key)) { - const value = await Storage.getItemAsync(key); - try { - LDS[key] = JSON.parse(value); - } catch (error) { - console.error('Error getAllContents: ', error); - } - } - return LDS; - }, Promise.resolve({})); - }, - - // Used for testing - async getRawStorage(): Object { - const keys = await Storage.getAllKeysAsync(); - return keys.reduce(async (previousPromise, key) => { - const LDS = await previousPromise; - const value = await Storage.getItemAsync(key); - LDS[key] = value; - return LDS; - }, Promise.resolve({})); - }, - - async clear(): Promise { - const keys = await Storage.getAllKeysAsync(); - - const toRemove = []; - for (const key of keys) { - if (isLocalDatastoreKey(key)) { - toRemove.push(key); - } - } - const promises = toRemove.map(this.unPinWithName); - return Promise.all(promises); - }, -}; - -module.exports = LocalDatastoreController; +if (process.env.PARSE_BUILD === 'react-native') { + module.exports = require('./LocalDatastoreController.react-native'); +} else { + module.exports = require('./LocalDatastoreController.default'); +} diff --git a/src/Parse.ts b/src/Parse.ts index dbf3a0983..30276051f 100644 --- a/src/Parse.ts +++ b/src/Parse.ts @@ -32,6 +32,9 @@ import Storage from './Storage' import User from './ParseUser' import LiveQuery from './ParseLiveQuery' import LiveQueryClient from './LiveQueryClient' +import LocalDatastoreController from './LocalDatastoreController'; +import StorageController from './StorageController'; +import WebSocketController from './WebSocketController'; /** * Contains all Parse API classes and functions. @@ -181,6 +184,10 @@ const Parse: ParseType = { CoreManager.set('MASTER_KEY', masterKey); CoreManager.set('USE_MASTER_KEY', false); CoreManager.setIfNeeded('EventEmitter', EventEmitter); + CoreManager.setIfNeeded('CryptoController', CryptoController); + CoreManager.setIfNeeded('LocalDatastoreController', LocalDatastoreController); + CoreManager.setIfNeeded('StorageController', StorageController); + CoreManager.setIfNeeded('WebSocketController', WebSocketController); Parse.LiveQuery = new LiveQuery(); CoreManager.setIfNeeded('LiveQuery', Parse.LiveQuery); @@ -433,7 +440,6 @@ const Parse: ParseType = { }, }; -CoreManager.setCryptoController(CryptoController); CoreManager.setInstallationController(InstallationController); CoreManager.setRESTController(RESTController); diff --git a/src/Storage.js b/src/Storage.js index 67392c249..8f7599ea5 100644 --- a/src/Storage.js +++ b/src/Storage.js @@ -97,13 +97,3 @@ const Storage = { module.exports = Storage; export default Storage; - -if (process.env.PARSE_BUILD === 'react-native') { - CoreManager.setStorageController(require('./StorageController.react-native')); -} else if (process.env.PARSE_BUILD === 'browser') { - CoreManager.setStorageController(require('./StorageController.browser')); -} else if (process.env.PARSE_BUILD === 'weapp') { - CoreManager.setStorageController(require('./StorageController.weapp')); -} else { - CoreManager.setStorageController(require('./StorageController.default')); -} diff --git a/src/StorageController.js b/src/StorageController.js new file mode 100644 index 000000000..33508d75b --- /dev/null +++ b/src/StorageController.js @@ -0,0 +1,9 @@ +if (process.env.PARSE_BUILD === 'react-native') { + module.exports = require('./StorageController.react-native'); +} else if (process.env.PARSE_BUILD === 'browser') { + module.exports = require('./StorageController.browser'); +} else if (process.env.PARSE_BUILD === 'weapp') { + module.exports = require('./StorageController.weapp'); +} else { + module.exports = require('./StorageController.default'); +} diff --git a/src/WebSocketController.js b/src/WebSocketController.js new file mode 100644 index 000000000..c3e1d0ca4 --- /dev/null +++ b/src/WebSocketController.js @@ -0,0 +1,18 @@ +/* global WebSocket */ + +let WebSocketController; + +try { + if (process.env.PARSE_BUILD === 'browser') { + WebSocketController = (typeof WebSocket === 'function' || typeof WebSocket === 'object' ? WebSocket : null); + } else if (process.env.PARSE_BUILD === 'node') { + WebSocketController = require('ws'); + } else if (process.env.PARSE_BUILD === 'weapp') { + WebSocketController = require('./Socket.weapp'); + } else if (process.env.PARSE_BUILD === 'react-native') { + WebSocketController = WebSocket; + } +} catch (_) { + // WebSocket unavailable +} +module.exports = WebSocketController; diff --git a/src/__tests__/InstallationController-test.js b/src/__tests__/InstallationController-test.js index 2085131c4..dcfad0a45 100644 --- a/src/__tests__/InstallationController-test.js +++ b/src/__tests__/InstallationController-test.js @@ -11,6 +11,8 @@ const CoreManager = require('../CoreManager'); const InstallationController = require('../InstallationController'); const Storage = require('../Storage'); +CoreManager.setStorageController(require('../StorageController.default')); + describe('InstallationController', () => { beforeEach(() => { CoreManager.set('APPLICATION_ID', 'A'); diff --git a/src/__tests__/LiveQueryClient-test.js b/src/__tests__/LiveQueryClient-test.js index 9cd4e4f5f..4588cc1cc 100644 --- a/src/__tests__/LiveQueryClient-test.js +++ b/src/__tests__/LiveQueryClient-test.js @@ -25,6 +25,7 @@ jest.dontMock('../ParseACL'); jest.dontMock('../ParseQuery'); jest.dontMock('../LiveQuerySubscription'); jest.dontMock('../LocalDatastore'); +jest.dontMock('../WebSocketController'); jest.useFakeTimers(); @@ -39,10 +40,12 @@ const EventEmitter = require('../EventEmitter'); const LiveQueryClient = require('../LiveQueryClient').default; const ParseObject = require('../ParseObject').default; const ParseQuery = require('../ParseQuery').default; +const WebSocketController = require('../WebSocketController'); const { resolvingPromise } = require('../promiseUtils'); const events = require('events'); CoreManager.setLocalDatastore(mockLocalDatastore); +CoreManager.setWebSocketController(WebSocketController); describe('LiveQueryClient', () => { beforeEach(() => { diff --git a/src/__tests__/ParseConfig-test.js b/src/__tests__/ParseConfig-test.js index a3aa5546b..e662bd490 100644 --- a/src/__tests__/ParseConfig-test.js +++ b/src/__tests__/ParseConfig-test.js @@ -16,9 +16,11 @@ const CoreManager = require('../CoreManager'); const ParseConfig = require('../ParseConfig').default; const ParseGeoPoint = require('../ParseGeoPoint').default; const Storage = require('../Storage'); +const StorageController = require('../StorageController.default'); CoreManager.set('APPLICATION_ID', 'A'); CoreManager.set('JAVASCRIPT_KEY', 'B'); +CoreManager.set('StorageController', StorageController); describe('ParseConfig', () => { beforeEach(() => { diff --git a/src/__tests__/ParseUser-test.js b/src/__tests__/ParseUser-test.js index 631842b8e..16f25e682 100644 --- a/src/__tests__/ParseUser-test.js +++ b/src/__tests__/ParseUser-test.js @@ -34,6 +34,7 @@ const flushPromises = require('./test_helpers/flushPromises'); const mockAsyncStorage = require('./test_helpers/mockAsyncStorage'); const CoreManager = require('../CoreManager'); const CryptoController = require('../CryptoController'); +const StorageController = require('../StorageController.default'); const LocalDatastore = require('../LocalDatastore'); const ParseObject = require('../ParseObject').default; const ParseUser = require('../ParseUser').default; @@ -44,6 +45,7 @@ const AnonymousUtils = require('../AnonymousUtils').default; CoreManager.set('APPLICATION_ID', 'A'); CoreManager.set('JAVASCRIPT_KEY', 'B'); CoreManager.setCryptoController(CryptoController); +CoreManager.setStorageController(StorageController); describe('ParseUser', () => { beforeEach(() => { diff --git a/src/__tests__/browser-test.js b/src/__tests__/browser-test.js index e89714204..80d84ad98 100644 --- a/src/__tests__/browser-test.js +++ b/src/__tests__/browser-test.js @@ -10,6 +10,7 @@ jest.dontMock('../Storage'); jest.dontMock('crypto-js/aes'); jest.setMock('../EventuallyQueue', { poll: jest.fn() }); +const CoreManager = require('../CoreManager'); const ParseError = require('../ParseError').default; const EventuallyQueue = require('../EventuallyQueue'); @@ -61,7 +62,9 @@ describe('Browser', () => { }); it('load StorageController', () => { - const StorageController = require('../StorageController.browser'); + const StorageController = require('../StorageController'); + CoreManager.setStorageController(StorageController); + jest.spyOn(StorageController, 'setItem'); const storage = require('../Storage'); storage.setItem('key', 'value'); diff --git a/src/__tests__/react-native-test.js b/src/__tests__/react-native-test.js index 02c0a1e76..731684760 100644 --- a/src/__tests__/react-native-test.js +++ b/src/__tests__/react-native-test.js @@ -8,7 +8,8 @@ jest.dontMock('../LiveQueryClient'); jest.dontMock('../LocalDatastore'); jest.dontMock('../ParseObject'); jest.dontMock('../Storage'); - +jest.dontMock('../LocalDatastoreController'); +jest.dontMock('../WebSocketController'); jest.mock( 'react-native/Libraries/vendor/emitter/EventEmitter', () => { @@ -54,14 +55,16 @@ describe('React Native', () => { }); it('load LocalDatastoreController', () => { - const LocalDatastoreController = require('../LocalDatastoreController.react-native'); + const LocalDatastoreController = require('../LocalDatastoreController'); require('../LocalDatastore'); const LDC = CoreManager.getLocalDatastoreController(); expect(LocalDatastoreController).toEqual(LDC); }); it('load StorageController', () => { - const StorageController = require('../StorageController.react-native'); + const StorageController = require('../StorageController'); + CoreManager.setStorageController(StorageController); + jest.spyOn(StorageController, 'setItemAsync'); const storage = require('../Storage'); storage.setItemAsync('key', 'value'); @@ -69,6 +72,9 @@ describe('React Native', () => { }); it('load WebSocketController', () => { + const WebSocketController = require('../WebSocketController'); + CoreManager.setWebSocketController(WebSocketController); + jest.mock('../EventEmitter', () => { return require('events').EventEmitter; }); diff --git a/src/__tests__/weapp-test.js b/src/__tests__/weapp-test.js index ca815502c..c693d087e 100644 --- a/src/__tests__/weapp-test.js +++ b/src/__tests__/weapp-test.js @@ -10,6 +10,7 @@ jest.dontMock('../ParseObject'); jest.dontMock('../RESTController'); jest.dontMock('../Socket.weapp'); jest.dontMock('../Storage'); +jest.dontMock('../StorageController.weapp'); jest.dontMock('../uuid'); jest.dontMock('crypto-js/aes'); jest.dontMock('./test_helpers/mockWeChat'); @@ -33,7 +34,8 @@ describe('WeChat', () => { }); it('load StorageController', () => { - const StorageController = require('../StorageController.weapp'); + const StorageController = require('../StorageController'); + CoreManager.setStorageController(StorageController); jest.spyOn(StorageController, 'setItem'); const storage = require('../Storage'); storage.setItem('key', 'value'); @@ -54,7 +56,9 @@ describe('WeChat', () => { }); it('load WebSocketController', () => { - const socket = require('../Socket.weapp'); + const socket = require('../WebSocketController'); + CoreManager.setWebSocketController(socket); + require('../LiveQueryClient'); const websocket = CoreManager.getWebSocketController(); expect(websocket).toEqual(socket); From 2d40e4b5312de75bf9b60c59db1f36c1ecbb58b9 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Fri, 12 Apr 2024 18:09:26 -0500 Subject: [PATCH 2/5] websocket test --- integration/test/ParseEventuallyQueueTest.js | 3 +-- integration/test/ParseReactNativeTest.js | 21 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/integration/test/ParseEventuallyQueueTest.js b/integration/test/ParseEventuallyQueueTest.js index 1d8157553..56e593652 100644 --- a/integration/test/ParseEventuallyQueueTest.js +++ b/integration/test/ParseEventuallyQueueTest.js @@ -247,8 +247,7 @@ describe('Parse EventuallyQueue', () => { } assert.strictEqual(Parse.EventuallyQueue.isPolling(), false); - length = await Parse.EventuallyQueue.length(); - while (length) { + while (await Parse.EventuallyQueue.length()) { await sleep(100); } length = await Parse.EventuallyQueue.length(); diff --git a/integration/test/ParseReactNativeTest.js b/integration/test/ParseReactNativeTest.js index 7c9140479..d7d30c68c 100644 --- a/integration/test/ParseReactNativeTest.js +++ b/integration/test/ParseReactNativeTest.js @@ -1,6 +1,7 @@ 'use strict'; const Parse = require('../../react-native'); +const { resolvingPromise } = require('../../lib/react-native/promiseUtils'); const CryptoController = require('../../lib/react-native/CryptoController'); const LocalDatastoreController = require('../../lib/react-native/LocalDatastoreController.default'); const StorageController = require('../../lib/react-native/StorageController.default'); @@ -80,4 +81,24 @@ describe('Parse React Native', () => { expect(cachedObject.objectId).toBe(object.id); expect(cachedObject.field).toBe('test'); }); + + it('can subscribe to query', async () => { + // Handle WebSocket Controller + const object = new Parse.Object('TestObject'); + await object.save(); + const installationId = await Parse.CoreManager.getInstallationController().currentInstallationId(); + + const query = new Parse.Query('TestObject'); + query.equalTo('objectId', object.id); + const subscription = await query.subscribe(); + const promise = resolvingPromise(); + subscription.on('update', (object, _, response) => { + expect(object.get('foo')).toBe('bar'); + expect(response.installationId).toBe(installationId); + promise.resolve(); + }); + object.set({ foo: 'bar' }); + await object.save(); + await promise; + }); }); From 045da1618e136e96b6a579225a74734edd37ab52 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Fri, 12 Apr 2024 18:18:50 -0500 Subject: [PATCH 3/5] fix flaky test --- integration/test/ParseEventuallyQueueTest.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration/test/ParseEventuallyQueueTest.js b/integration/test/ParseEventuallyQueueTest.js index 56e593652..51035945a 100644 --- a/integration/test/ParseEventuallyQueueTest.js +++ b/integration/test/ParseEventuallyQueueTest.js @@ -247,10 +247,10 @@ describe('Parse EventuallyQueue', () => { } assert.strictEqual(Parse.EventuallyQueue.isPolling(), false); - while (await Parse.EventuallyQueue.length()) { - await sleep(100); - } length = await Parse.EventuallyQueue.length(); + while (length) { + length = await Parse.EventuallyQueue.length(); + } assert.strictEqual(length, 0); const query = new Parse.Query('TestObject'); From 2d838b54b4c1e13668d6c85f3e88bf3810ea1002 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Sat, 13 Apr 2024 17:49:01 -0500 Subject: [PATCH 4/5] Properly handle Parse.LiveQuery --- src/Parse.ts | 20 ++++++++++++++------ src/__tests__/Parse-test.js | 13 +++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Parse.ts b/src/Parse.ts index 30276051f..6c1c5a9cd 100644 --- a/src/Parse.ts +++ b/src/Parse.ts @@ -30,7 +30,7 @@ import Schema from './ParseSchema' import Session from './ParseSession' import Storage from './Storage' import User from './ParseUser' -import LiveQuery from './ParseLiveQuery' +import ParseLiveQuery from './ParseLiveQuery' import LiveQueryClient from './LiveQueryClient' import LocalDatastoreController from './LocalDatastoreController'; import StorageController from './StorageController'; @@ -81,7 +81,7 @@ interface ParseType { Session: typeof Session, Storage: typeof Storage, User: typeof User, - LiveQuery?: typeof LiveQuery, + LiveQuery: ParseLiveQuery, LiveQueryClient: typeof LiveQueryClient, initialize(applicationId: string, javaScriptKey: string): void, @@ -149,7 +149,6 @@ const Parse: ParseType = { Storage: Storage, User: User, LiveQueryClient: LiveQueryClient, - LiveQuery: undefined, IndexedDB: undefined, Hooks: undefined, Parse: undefined, @@ -184,14 +183,12 @@ const Parse: ParseType = { CoreManager.set('MASTER_KEY', masterKey); CoreManager.set('USE_MASTER_KEY', false); CoreManager.setIfNeeded('EventEmitter', EventEmitter); + CoreManager.setIfNeeded('LiveQuery', new ParseLiveQuery()); CoreManager.setIfNeeded('CryptoController', CryptoController); CoreManager.setIfNeeded('LocalDatastoreController', LocalDatastoreController); CoreManager.setIfNeeded('StorageController', StorageController); CoreManager.setIfNeeded('WebSocketController', WebSocketController); - Parse.LiveQuery = new LiveQuery(); - CoreManager.setIfNeeded('LiveQuery', Parse.LiveQuery); - if (process.env.PARSE_BUILD === 'browser') { Parse.IndexedDB = CoreManager.setIfNeeded('IndexedDBStorageController', IndexedDBStorageController); } @@ -296,6 +293,17 @@ const Parse: ParseType = { return CoreManager.get('SERVER_AUTH_TYPE'); }, + /** + * @member {ParseLiveQuery} Parse.LiveQuery + * @static + */ + set LiveQuery(liveQuery: ParseLiveQuery) { + CoreManager.setLiveQuery(liveQuery); + }, + get LiveQuery() { + return CoreManager.getLiveQuery(); + }, + /** * @member {string} Parse.liveQueryServerURL * @static diff --git a/src/__tests__/Parse-test.js b/src/__tests__/Parse-test.js index 560585230..7c150318d 100644 --- a/src/__tests__/Parse-test.js +++ b/src/__tests__/Parse-test.js @@ -3,15 +3,20 @@ jest.dontMock('../CryptoController'); jest.dontMock('../decode'); jest.dontMock('../encode'); jest.dontMock('../Parse'); +jest.dontMock('../ParseObject'); +jest.dontMock('../ParseLiveQuery'); jest.dontMock('../LocalDatastore'); jest.dontMock('crypto-js/aes'); jest.setMock('../EventuallyQueue', { poll: jest.fn() }); global.indexedDB = require('./test_helpers/mockIndexedDB'); const CoreManager = require('../CoreManager'); +const ParseLiveQuery = require('../ParseLiveQuery').default; const EventuallyQueue = require('../EventuallyQueue'); const Parse = require('../Parse'); +CoreManager.setEventEmitter(require('events').EventEmitter); + describe('Parse module', () => { it('can be initialized with keys', () => { Parse.initialize('A', 'B'); @@ -173,6 +178,14 @@ describe('Parse module', () => { CoreManager.set('REQUEST_BATCH_SIZE', 20); }); + it('can set and get live query', () => { + const LiveQuery = new ParseLiveQuery(); + expect(Parse.LiveQuery).toEqual(undefined); + Parse.LiveQuery = LiveQuery + expect(Parse.LiveQuery).toEqual(LiveQuery); + Parse.LiveQuery = undefined; + }); + it('can set allowCustomObjectId', () => { expect(Parse.allowCustomObjectId).toBe(false); Parse.allowCustomObjectId = true; From b14942d8de7aa3bfa5c30782a78d604d6c403f7c Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Sat, 13 Apr 2024 17:52:03 -0500 Subject: [PATCH 5/5] fix tests --- src/__tests__/Parse-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/Parse-test.js b/src/__tests__/Parse-test.js index 7c150318d..6a9e19737 100644 --- a/src/__tests__/Parse-test.js +++ b/src/__tests__/Parse-test.js @@ -179,11 +179,11 @@ describe('Parse module', () => { }); it('can set and get live query', () => { + const temp = Parse.LiveQuery; const LiveQuery = new ParseLiveQuery(); - expect(Parse.LiveQuery).toEqual(undefined); Parse.LiveQuery = LiveQuery expect(Parse.LiveQuery).toEqual(LiveQuery); - Parse.LiveQuery = undefined; + Parse.LiveQuery = temp; }); it('can set allowCustomObjectId', () => {