Skip to content

feat: Add support for custom EventEmitter #1999

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 40 additions & 27 deletions integration/test/ParseLocalDatastoreTest.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,52 @@
'use strict';

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

global.localStorage = require('./mockLocalStorage');
global.WebSocket = require('ws');
const mockRNStorage = require('./mockRNStorage');
const LocalDatastoreUtils = require('../../lib/node/LocalDatastoreUtils');
const serverURL = 'http://localhost:1337/parse';

const { DEFAULT_PIN, PIN_PREFIX, isLocalDatastoreKey } = LocalDatastoreUtils;
function runTest(controller) {
const Parse = require(`../../${controller.name}`);
const LocalDatastoreUtils = require('../../lib/node/LocalDatastoreUtils');

function LDS_KEY(object) {
return Parse.LocalDatastore.getKeyForObject(object);
}
function LDS_FULL_JSON(object) {
const json = object._toFullJSON();
if (object._localId) {
json._localId = object._localId;
const { DEFAULT_PIN, PIN_PREFIX, isLocalDatastoreKey } = LocalDatastoreUtils;

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

function LDS_KEY(object) {
return Parse.LocalDatastore.getKeyForObject(object);
}
return json;
}
function runTest(controller) {
function LDS_FULL_JSON(object) {
const json = object._toFullJSON();
if (object._localId) {
json._localId = object._localId;
}
return json;
}

describe(`Parse Object Pinning (${controller.name})`, () => {
beforeEach(async () => {
const StorageController = require(controller.file);
Parse.CoreManager.setAsyncStorage(mockRNStorage);
Parse.CoreManager.setLocalDatastoreController(StorageController);
Parse.enableLocalDatastore();
Parse.CoreManager.setEventEmitter(require('events').EventEmitter);
Parse.User.enableUnsafeCurrentUser();
await Parse.LocalDatastore._clear();
Parse.initialize('integration');
Parse.CoreManager.set('SERVER_URL', serverURL);
Parse.CoreManager.set('MASTER_KEY', 'notsosecret');
const RESTController = Parse.CoreManager.getRESTController();
RESTController._setXHR(require('xmlhttprequest').XMLHttpRequest);
Parse.enableLocalDatastore();
});

function getStorageCount(storage) {
return Object.keys(storage).reduce((acc, key) => acc + (isLocalDatastoreKey(key) ? 1 : 0), 1);
}

it(`${controller.name} can clear localDatastore`, async () => {
const obj1 = new TestObject();
const obj2 = new TestObject();
Expand Down Expand Up @@ -1060,8 +1076,15 @@ function runTest(controller) {
const StorageController = require(controller.file);
Parse.CoreManager.setAsyncStorage(mockRNStorage);
Parse.CoreManager.setLocalDatastoreController(StorageController);
Parse.enableLocalDatastore();
Parse.CoreManager.setEventEmitter(require('events').EventEmitter);
Parse.LocalDatastore._clear();
Parse.User.enableUnsafeCurrentUser();
Parse.initialize('integration');
Parse.CoreManager.set('SERVER_URL', serverURL);
Parse.CoreManager.set('MASTER_KEY', 'notsosecret');
const RESTController = Parse.CoreManager.getRESTController();
RESTController._setXHR(require('xmlhttprequest').XMLHttpRequest);
Parse.enableLocalDatastore();

const numbers = [];
for (let i = 0; i < 10; i++) {
Expand Down Expand Up @@ -2949,20 +2972,10 @@ function runTest(controller) {
}

describe('Parse LocalDatastore', () => {
beforeEach(() => {
Parse.CoreManager.getInstallationController()._setInstallationIdCache('1234');
Parse.enableLocalDatastore();
Parse.User.enableUnsafeCurrentUser();
});

const controllers = [
{ name: 'Default', file: '../../lib/node/LocalDatastoreController' },
{
name: 'React-Native',
file: '../../lib/node/LocalDatastoreController.react-native',
},
{ name: 'node', file: '../../lib/node/LocalDatastoreController' },
{ name: 'react-native', file: '../../lib/react-native/LocalDatastoreController.react-native' },
];

for (let i = 0; i < controllers.length; i += 1) {
const controller = controllers[i];
runTest(controller);
Expand Down
23 changes: 23 additions & 0 deletions src/CoreManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ const CoreManager = {
config[key] = value;
},

setIfNeeded: function (key: string, value: any): any {
if (!config.hasOwnProperty(key)) {
config[key] = value;
}
return config[key];
},

/* Specialized Controller Setters/Getters */

setAnalyticsController(controller: AnalyticsController) {
Expand Down Expand Up @@ -255,6 +262,14 @@ const CoreManager = {
return config['CryptoController'];
},

setEventEmitter(eventEmitter: any) {
config['EventEmitter'] = eventEmitter;
},

getEventEmitter(): any {
return config['EventEmitter'];
},

setFileController(controller: FileController) {
requireMethods('FileController', ['saveFile', 'saveBase64'], controller);
config['FileController'] = controller;
Expand All @@ -273,6 +288,14 @@ const CoreManager = {
return config['InstallationController'];
},

setLiveQuery(liveQuery: any) {
config['LiveQuery'] = liveQuery;
},

getLiveQuery(): any {
return config['LiveQuery'];
},

setObjectController(controller: ObjectController) {
requireMethods('ObjectController', ['save', 'fetch', 'destroy'], controller);
config['ObjectController'] = controller;
Expand Down
22 changes: 14 additions & 8 deletions src/EventEmitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
* This is a simple wrapper to unify EventEmitter implementations across platforms.
*/

if (process.env.PARSE_BUILD === 'react-native') {
let EventEmitter = require('react-native/Libraries/vendor/emitter/EventEmitter');
if (EventEmitter.default) {
EventEmitter = EventEmitter.default;
let EventEmitter;

try {
if (process.env.PARSE_BUILD === 'react-native') {
EventEmitter = require('react-native/Libraries/vendor/emitter/EventEmitter');
if (EventEmitter.default) {
EventEmitter = EventEmitter.default;
}
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
} else {
EventEmitter = require('events').EventEmitter;
}
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
module.exports = EventEmitter;
} else {
module.exports = require('events').EventEmitter;
} catch (_) {
// EventEmitter unavailable
}
module.exports = EventEmitter;
10 changes: 5 additions & 5 deletions src/LiveQueryClient.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* global WebSocket */

import CoreManager from './CoreManager';
import EventEmitter from './EventEmitter';
import ParseObject from './ParseObject';
import LiveQuerySubscription from './LiveQuerySubscription';
import { resolvingPromise } from './promiseUtils';
Expand Down Expand Up @@ -63,7 +62,6 @@ const generateInterval = k => {

/**
* Creates a new LiveQueryClient.
* Extends events.EventEmitter
* <a href="https://nodejs.org/api/events.html#events_class_eventemitter">cloud functions</a>.
*
* A wrapper of a standard WebSocket client. We add several useful methods to
Expand Down Expand Up @@ -105,7 +103,7 @@ const generateInterval = k => {
*
* @alias Parse.LiveQueryClient
*/
class LiveQueryClient extends EventEmitter {
class LiveQueryClient {
attempts: number;
id: number;
requestId: number;
Expand Down Expand Up @@ -138,8 +136,6 @@ class LiveQueryClient extends EventEmitter {
sessionToken,
installationId,
}) {
super();

if (!serverURL || serverURL.indexOf('ws') !== 0) {
throw new Error(
'You need to set a proper Parse LiveQuery server url before using LiveQueryClient'
Expand All @@ -160,7 +156,11 @@ class LiveQueryClient extends EventEmitter {
this.connectPromise = resolvingPromise();
this.subscriptions = new Map();
this.state = CLIENT_STATE.INITIALIZED;
const EventEmitter = CoreManager.getEventEmitter();
this.emitter = new EventEmitter();

this.on = this.emitter.on;
this.emit = this.emitter.emit;
// adding listener so process does not crash
// best practice is for developer to register their own listener
this.on('error', () => {});
Expand Down
11 changes: 5 additions & 6 deletions src/LiveQuerySubscription.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import EventEmitter from './EventEmitter';
import CoreManager from './CoreManager';
import { resolvingPromise } from './promiseUtils';

/**
* Creates a new LiveQuery Subscription.
* Extends events.EventEmitter
* <a href="https://nodejs.org/api/events.html#events_class_eventemitter">cloud functions</a>.
*
* <p>Response Object - Contains data from the client that made the request
Expand Down Expand Up @@ -84,24 +82,25 @@ import { resolvingPromise } from './promiseUtils';
* subscription.on('close', () => {
*
* });</pre></p>
*
* @alias Parse.LiveQuerySubscription
*/
class Subscription extends EventEmitter {
class Subscription {
/*
* @param {string} id - subscription id
* @param {string} query - query to subscribe to
* @param {string} sessionToken - optional session token
*/
constructor(id, query, sessionToken) {
super();
this.id = id;
this.query = query;
this.sessionToken = sessionToken;
this.subscribePromise = resolvingPromise();
this.unsubscribePromise = resolvingPromise();
this.subscribed = false;
const EventEmitter = CoreManager.getEventEmitter();
this.emitter = new EventEmitter();

this.on = this.emitter.on;
this.emit = this.emitter.emit;
// adding listener so process does not crash
// best practice is for developer to register their own listener
this.on('error', () => {});
Expand Down
11 changes: 7 additions & 4 deletions src/Parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import AnonymousUtils from './AnonymousUtils'
import * as Cloud from './Cloud';
import CLP from './ParseCLP';
import CoreManager from './CoreManager';
import EventEmitter from './EventEmitter';
import Config from './ParseConfig'
import ParseError from './ParseError'
import FacebookUtils from './FacebookUtils'
Expand Down Expand Up @@ -76,7 +77,7 @@ interface ParseType {
Session: typeof Session,
Storage: typeof Storage,
User: typeof User,
LiveQuery: typeof LiveQuery,
LiveQuery?: typeof LiveQuery,
LiveQueryClient: typeof LiveQueryClient,

initialize(applicationId: string, javaScriptKey: string): void,
Expand Down Expand Up @@ -143,13 +144,12 @@ const Parse: ParseType = {
Session: Session,
Storage: Storage,
User: User,
LiveQuery: LiveQuery,
LiveQueryClient: LiveQueryClient,
LiveQuery: undefined,
IndexedDB: undefined,
Hooks: undefined,
Parse: undefined,


/**
* Call this method first to set up your authentication tokens for Parse.
*
Expand Down Expand Up @@ -179,6 +179,10 @@ const Parse: ParseType = {
CoreManager.set('JAVASCRIPT_KEY', javaScriptKey);
CoreManager.set('MASTER_KEY', masterKey);
CoreManager.set('USE_MASTER_KEY', false);
CoreManager.setIfNeeded('EventEmitter', EventEmitter);

Parse.LiveQuery = new LiveQuery();
CoreManager.setIfNeeded('LiveQuery', Parse.LiveQuery);
},

/**
Expand Down Expand Up @@ -422,7 +426,6 @@ const Parse: ParseType = {
isEncryptedUserEnabled () {
return this.encryptedUser;
},

};

if (process.env.PARSE_BUILD === 'browser') {
Expand Down
Loading