Use Prettier JS (#5017)

* Adds prettier

* Run lint before tests
This commit is contained in:
Florent Vilmart
2018-09-01 13:58:06 -04:00
committed by GitHub
parent 189cd259ee
commit d83a0b6808
240 changed files with 41098 additions and 29020 deletions

View File

@@ -3,7 +3,13 @@ import logger from '../logger';
import type { FlattenedObjectData } from './Subscription';
export type Message = { [attr: string]: any };
const dafaultFields = ['className', 'objectId', 'updatedAt', 'createdAt', 'ACL'];
const dafaultFields = [
'className',
'objectId',
'updatedAt',
'createdAt',
'ACL',
];
class Client {
id: number;
@@ -42,13 +48,21 @@ class Client {
parseWebSocket.send(message);
}
static pushError(parseWebSocket: any, code: number, error: string, reconnect: boolean = true): void {
Client.pushResponse(parseWebSocket, JSON.stringify({
'op': 'error',
'error': error,
'code': code,
'reconnect': reconnect
}));
static pushError(
parseWebSocket: any,
code: number,
error: string,
reconnect: boolean = true
): void {
Client.pushResponse(
parseWebSocket,
JSON.stringify({
op: 'error',
error: error,
code: code,
reconnect: reconnect,
})
);
}
addSubscriptionInfo(requestId: number, subscriptionInfo: any): void {
@@ -66,8 +80,8 @@ class Client {
_pushEvent(type: string): Function {
return function(subscriptionId: number, parseObjectJSON: any): void {
const response: Message = {
'op' : type,
'clientId' : this.id
op: type,
clientId: this.id,
};
if (typeof subscriptionId !== 'undefined') {
response['requestId'] = subscriptionId;
@@ -80,7 +94,7 @@ class Client {
response['object'] = this._toJSONWithFields(parseObjectJSON, fields);
}
Client.pushResponse(this.parseWebSocket, JSON.stringify(response));
}
};
}
_toJSONWithFields(parseObjectJSON: any, fields: any): FlattenedObjectData {
@@ -100,6 +114,4 @@ class Client {
}
}
export {
Client
}
export { Client };

View File

@@ -1,5 +1,5 @@
import { ParsePubSub } from './ParsePubSub';
import Parse from 'parse/node';
import Parse from 'parse/node';
import logger from '../logger';
class ParseCloudCodePublisher {
@@ -21,11 +21,15 @@ class ParseCloudCodePublisher {
// Request is the request object from cloud code functions. request.object is a ParseObject.
_onCloudCodeMessage(type: string, request: any): void {
logger.verbose('Raw request from cloud code current : %j | original : %j', request.object, request.original);
logger.verbose(
'Raw request from cloud code current : %j | original : %j',
request.object,
request.original
);
// We need the full JSON which includes className
const message = {
currentParseObject: request.object._toFullJSON()
}
currentParseObject: request.object._toFullJSON(),
};
if (request.original) {
message.originalParseObject = request.original._toFullJSON();
}
@@ -33,6 +37,4 @@ class ParseCloudCodePublisher {
}
}
export {
ParseCloudCodePublisher
}
export { ParseCloudCodePublisher };

View File

@@ -17,7 +17,7 @@ class ParseLiveQueryServer {
// className -> (queryHash -> subscription)
subscriptions: Object;
parseWebSocketServer: Object;
keyPairs : any;
keyPairs: any;
// The subscriber we use to get object update from publisher
subscriber: Object;
@@ -49,7 +49,7 @@ class ParseLiveQueryServer {
// Initialize websocket server
this.parseWebSocketServer = new ParseWebSocketServer(
server,
(parseWebsocket) => this._onConnect(parseWebsocket),
parseWebsocket => this._onConnect(parseWebsocket),
config.websocketTimeout
);
@@ -64,7 +64,7 @@ class ParseLiveQueryServer {
let message;
try {
message = JSON.parse(messageStr);
} catch(e) {
} catch (e) {
logger.error('unable to parse message', messageStr, e);
return;
}
@@ -74,7 +74,11 @@ class ParseLiveQueryServer {
} else if (channel === Parse.applicationId + 'afterDelete') {
this._onAfterDelete(message);
} else {
logger.error('Get message %s from unknown channel %j', message, channel);
logger.error(
'Get message %s from unknown channel %j',
message,
channel
);
}
});
@@ -108,7 +112,11 @@ class ParseLiveQueryServer {
const deletedParseObject = message.currentParseObject.toJSON();
const className = deletedParseObject.className;
logger.verbose('ClassName: %j | ObjectId: %s', className, deletedParseObject.id);
logger.verbose(
'ClassName: %j | ObjectId: %s',
className,
deletedParseObject.id
);
logger.verbose('Current client number : %d', this.clients.size);
const classSubscriptions = this.subscriptions.get(className);
@@ -117,11 +125,16 @@ class ParseLiveQueryServer {
return;
}
for (const subscription of classSubscriptions.values()) {
const isSubscriptionMatched = this._matchesSubscription(deletedParseObject, subscription);
const isSubscriptionMatched = this._matchesSubscription(
deletedParseObject,
subscription
);
if (!isSubscriptionMatched) {
continue;
}
for (const [clientId, requestIds] of _.entries(subscription.clientRequestIds)) {
for (const [clientId, requestIds] of _.entries(
subscription.clientRequestIds
)) {
const client = this.clients.get(clientId);
if (typeof client === 'undefined') {
continue;
@@ -129,14 +142,17 @@ class ParseLiveQueryServer {
for (const requestId of requestIds) {
const acl = message.currentParseObject.getACL();
// Check ACL
this._matchesACL(acl, client, requestId).then((isMatched) => {
if (!isMatched) {
return null;
this._matchesACL(acl, client, requestId).then(
isMatched => {
if (!isMatched) {
return null;
}
client.pushDelete(requestId, deletedParseObject);
},
error => {
logger.error('Matching ACL error : ', error);
}
client.pushDelete(requestId, deletedParseObject);
}, (error) => {
logger.error('Matching ACL error : ', error);
});
);
}
}
}
@@ -153,7 +169,11 @@ class ParseLiveQueryServer {
}
const currentParseObject = message.currentParseObject.toJSON();
const className = currentParseObject.className;
logger.verbose('ClassName: %s | ObjectId: %s', className, currentParseObject.id);
logger.verbose(
'ClassName: %s | ObjectId: %s',
className,
currentParseObject.id
);
logger.verbose('Current client number : %d', this.clients.size);
const classSubscriptions = this.subscriptions.get(className);
@@ -162,9 +182,17 @@ class ParseLiveQueryServer {
return;
}
for (const subscription of classSubscriptions.values()) {
const isOriginalSubscriptionMatched = this._matchesSubscription(originalParseObject, subscription);
const isCurrentSubscriptionMatched = this._matchesSubscription(currentParseObject, subscription);
for (const [clientId, requestIds] of _.entries(subscription.clientRequestIds)) {
const isOriginalSubscriptionMatched = this._matchesSubscription(
originalParseObject,
subscription
);
const isCurrentSubscriptionMatched = this._matchesSubscription(
currentParseObject,
subscription
);
for (const [clientId, requestIds] of _.entries(
subscription.clientRequestIds
)) {
const client = this.clients.get(clientId);
if (typeof client === 'undefined') {
continue;
@@ -180,7 +208,11 @@ class ParseLiveQueryServer {
if (message.originalParseObject) {
originalACL = message.originalParseObject.getACL();
}
originalACLCheckingPromise = this._matchesACL(originalACL, client, requestId);
originalACLCheckingPromise = this._matchesACL(
originalACL,
client,
requestId
);
}
// Set current ParseObject ACL checking promise, if the object does not match
// subscription, we do not need to check ACL
@@ -189,56 +221,62 @@ class ParseLiveQueryServer {
currentACLCheckingPromise = Promise.resolve(false);
} else {
const currentACL = message.currentParseObject.getACL();
currentACLCheckingPromise = this._matchesACL(currentACL, client, requestId);
currentACLCheckingPromise = this._matchesACL(
currentACL,
client,
requestId
);
}
Promise.all(
[
originalACLCheckingPromise,
currentACLCheckingPromise
]
).then(([isOriginalMatched, isCurrentMatched]) => {
logger.verbose('Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s',
originalParseObject,
currentParseObject,
isOriginalSubscriptionMatched,
isCurrentSubscriptionMatched,
isOriginalMatched,
isCurrentMatched,
subscription.hash
);
Promise.all([
originalACLCheckingPromise,
currentACLCheckingPromise,
]).then(
([isOriginalMatched, isCurrentMatched]) => {
logger.verbose(
'Original %j | Current %j | Match: %s, %s, %s, %s | Query: %s',
originalParseObject,
currentParseObject,
isOriginalSubscriptionMatched,
isCurrentSubscriptionMatched,
isOriginalMatched,
isCurrentMatched,
subscription.hash
);
// Decide event type
let type;
if (isOriginalMatched && isCurrentMatched) {
type = 'Update';
} else if (isOriginalMatched && !isCurrentMatched) {
type = 'Leave';
} else if (!isOriginalMatched && isCurrentMatched) {
if (originalParseObject) {
type = 'Enter';
// Decide event type
let type;
if (isOriginalMatched && isCurrentMatched) {
type = 'Update';
} else if (isOriginalMatched && !isCurrentMatched) {
type = 'Leave';
} else if (!isOriginalMatched && isCurrentMatched) {
if (originalParseObject) {
type = 'Enter';
} else {
type = 'Create';
}
} else {
type = 'Create';
return null;
}
} else {
return null;
const functionName = 'push' + type;
client[functionName](requestId, currentParseObject);
},
error => {
logger.error('Matching ACL error : ', error);
}
const functionName = 'push' + type;
client[functionName](requestId, currentParseObject);
}, (error) => {
logger.error('Matching ACL error : ', error);
});
);
}
}
}
}
_onConnect(parseWebsocket: any): void {
parseWebsocket.on('message', (request) => {
parseWebsocket.on('message', request => {
if (typeof request === 'string') {
try {
request = JSON.parse(request);
} catch(e) {
} catch (e) {
logger.error('unable to parse request', request, e);
return;
}
@@ -246,28 +284,31 @@ class ParseLiveQueryServer {
logger.verbose('Request: %j', request);
// Check whether this request is a valid request, return error directly if not
if (!tv4.validate(request, RequestSchema['general']) || !tv4.validate(request, RequestSchema[request.op])) {
if (
!tv4.validate(request, RequestSchema['general']) ||
!tv4.validate(request, RequestSchema[request.op])
) {
Client.pushError(parseWebsocket, 1, tv4.error.message);
logger.error('Connect message error %s', tv4.error.message);
return;
}
switch(request.op) {
case 'connect':
this._handleConnect(parseWebsocket, request);
break;
case 'subscribe':
this._handleSubscribe(parseWebsocket, request);
break;
case 'update':
this._handleUpdateSubscription(parseWebsocket, request);
break;
case 'unsubscribe':
this._handleUnsubscribe(parseWebsocket, request);
break;
default:
Client.pushError(parseWebsocket, 3, 'Get unknown operation');
logger.error('Get unknown operation', request.op);
switch (request.op) {
case 'connect':
this._handleConnect(parseWebsocket, request);
break;
case 'subscribe':
this._handleSubscribe(parseWebsocket, request);
break;
case 'update':
this._handleUpdateSubscription(parseWebsocket, request);
break;
case 'unsubscribe':
this._handleUnsubscribe(parseWebsocket, request);
break;
default:
Client.pushError(parseWebsocket, 3, 'Get unknown operation');
logger.error('Get unknown operation', request.op);
}
});
@@ -279,7 +320,7 @@ class ParseLiveQueryServer {
event: 'ws_disconnect_error',
clients: this.clients.size,
subscriptions: this.subscriptions.size,
error: `Unable to find client ${clientId}`
error: `Unable to find client ${clientId}`,
});
logger.error(`Can not find client ${clientId} on disconnect`);
return;
@@ -290,12 +331,16 @@ class ParseLiveQueryServer {
this.clients.delete(clientId);
// Delete client from subscriptions
for (const [requestId, subscriptionInfo] of _.entries(client.subscriptionInfos)) {
for (const [requestId, subscriptionInfo] of _.entries(
client.subscriptionInfos
)) {
const subscription = subscriptionInfo.subscription;
subscription.deleteClientSubscription(clientId, requestId);
// If there is no client which is subscribing this subscription, remove it from subscriptions
const classSubscriptions = this.subscriptions.get(subscription.className);
const classSubscriptions = this.subscriptions.get(
subscription.className
);
if (!subscription.hasSubscribingClient()) {
classSubscriptions.delete(subscription.hash);
}
@@ -310,14 +355,14 @@ class ParseLiveQueryServer {
runLiveQueryEventHandlers({
event: 'ws_disconnect',
clients: this.clients.size,
subscriptions: this.subscriptions.size
subscriptions: this.subscriptions.size,
});
});
runLiveQueryEventHandlers({
event: 'ws_connect',
clients: this.clients.size,
subscriptions: this.subscriptions.size
subscriptions: this.subscriptions.size,
});
}
@@ -341,80 +386,86 @@ class ParseLiveQueryServer {
}
const subscriptionSessionToken = subscriptionInfo.sessionToken;
return this.sessionTokenCache.getUserId(subscriptionSessionToken).then((userId) => {
return acl.getReadAccess(userId);
}).then((isSubscriptionSessionTokenMatched) => {
if (isSubscriptionSessionTokenMatched) {
return Promise.resolve(true);
}
// Check if the user has any roles that match the ACL
return new Promise((resolve, reject) => {
// Resolve false right away if the acl doesn't have any roles
const acl_has_roles = Object.keys(acl.permissionsById).some(key => key.startsWith("role:"));
if (!acl_has_roles) {
return resolve(false);
return this.sessionTokenCache
.getUserId(subscriptionSessionToken)
.then(userId => {
return acl.getReadAccess(userId);
})
.then(isSubscriptionSessionTokenMatched => {
if (isSubscriptionSessionTokenMatched) {
return Promise.resolve(true);
}
this.sessionTokenCache.getUserId(subscriptionSessionToken)
.then((userId) => {
// Check if the user has any roles that match the ACL
return new Promise((resolve, reject) => {
// Resolve false right away if the acl doesn't have any roles
const acl_has_roles = Object.keys(acl.permissionsById).some(key =>
key.startsWith('role:')
);
if (!acl_has_roles) {
return resolve(false);
}
// Pass along a null if there is no user id
if (!userId) {
return Promise.resolve(null);
}
// Prepare a user object to query for roles
// To eliminate a query for the user, create one locally with the id
var user = new Parse.User();
user.id = userId;
return user;
})
.then((user) => {
// Pass along an empty array (of roles) if no user
if (!user) {
return Promise.resolve([]);
}
// Then get the user's roles
var rolesQuery = new Parse.Query(Parse.Role);
rolesQuery.equalTo("users", user);
return rolesQuery.find({useMasterKey:true});
}).
then((roles) => {
// Finally, see if any of the user's roles allow them read access
for (const role of roles) {
if (acl.getRoleReadAccess(role)) {
return resolve(true);
this.sessionTokenCache
.getUserId(subscriptionSessionToken)
.then(userId => {
// Pass along a null if there is no user id
if (!userId) {
return Promise.resolve(null);
}
}
resolve(false);
})
.catch((error) => {
reject(error);
// Prepare a user object to query for roles
// To eliminate a query for the user, create one locally with the id
var user = new Parse.User();
user.id = userId;
return user;
})
.then(user => {
// Pass along an empty array (of roles) if no user
if (!user) {
return Promise.resolve([]);
}
// Then get the user's roles
var rolesQuery = new Parse.Query(Parse.Role);
rolesQuery.equalTo('users', user);
return rolesQuery.find({ useMasterKey: true });
})
.then(roles => {
// Finally, see if any of the user's roles allow them read access
for (const role of roles) {
if (acl.getRoleReadAccess(role)) {
return resolve(true);
}
}
resolve(false);
})
.catch(error => {
reject(error);
});
});
})
.then(isRoleMatched => {
if (isRoleMatched) {
return Promise.resolve(true);
}
// Check client sessionToken matches ACL
const clientSessionToken = client.sessionToken;
return this.sessionTokenCache
.getUserId(clientSessionToken)
.then(userId => {
return acl.getReadAccess(userId);
});
});
}).then((isRoleMatched) => {
if(isRoleMatched) {
return Promise.resolve(true);
}
// Check client sessionToken matches ACL
const clientSessionToken = client.sessionToken;
return this.sessionTokenCache.getUserId(clientSessionToken).then((userId) => {
return acl.getReadAccess(userId);
});
}).then((isMatched) => {
return Promise.resolve(isMatched);
}, () => {
return Promise.resolve(false);
});
})
.then(
isMatched => {
return Promise.resolve(isMatched);
},
() => {
return Promise.resolve(false);
}
);
}
_handleConnect(parseWebsocket: any, request: any): any {
@@ -433,19 +484,22 @@ class ParseLiveQueryServer {
runLiveQueryEventHandlers({
event: 'connect',
clients: this.clients.size,
subscriptions: this.subscriptions.size
subscriptions: this.subscriptions.size,
});
}
_hasMasterKey(request: any, validKeyPairs: any): boolean {
if(!validKeyPairs || validKeyPairs.size == 0 ||
!validKeyPairs.has("masterKey")) {
if (
!validKeyPairs ||
validKeyPairs.size == 0 ||
!validKeyPairs.has('masterKey')
) {
return false;
}
if(!request || !request.hasOwnProperty("masterKey")) {
if (!request || !request.hasOwnProperty('masterKey')) {
return false;
}
return request.masterKey === validKeyPairs.get("masterKey");
return request.masterKey === validKeyPairs.get('masterKey');
}
_validateKeys(request: any, validKeyPairs: any): boolean {
@@ -466,8 +520,14 @@ class ParseLiveQueryServer {
_handleSubscribe(parseWebsocket: any, request: any): any {
// If we can not find this client, return error to client
if (!parseWebsocket.hasOwnProperty('clientId')) {
Client.pushError(parseWebsocket, 2, 'Can not find this client, make sure you connect to server before subscribing');
logger.error('Can not find this client, make sure you connect to server before subscribing');
Client.pushError(
parseWebsocket,
2,
'Can not find this client, make sure you connect to server before subscribing'
);
logger.error(
'Can not find this client, make sure you connect to server before subscribing'
);
return;
}
const client = this.clients.get(parseWebsocket.clientId);
@@ -484,13 +544,17 @@ class ParseLiveQueryServer {
if (classSubscriptions.has(subscriptionHash)) {
subscription = classSubscriptions.get(subscriptionHash);
} else {
subscription = new Subscription(className, request.query.where, subscriptionHash);
subscription = new Subscription(
className,
request.query.where,
subscriptionHash
);
classSubscriptions.set(subscriptionHash, subscription);
}
// Add subscriptionInfo to client
const subscriptionInfo = {
subscription: subscription
subscription: subscription,
};
// Add selected fields and sessionToken for this subscription if necessary
if (request.query.fields) {
@@ -502,16 +566,23 @@ class ParseLiveQueryServer {
client.addSubscriptionInfo(request.requestId, subscriptionInfo);
// Add clientId to subscription
subscription.addClientSubscription(parseWebsocket.clientId, request.requestId);
subscription.addClientSubscription(
parseWebsocket.clientId,
request.requestId
);
client.pushSubscribe(request.requestId);
logger.verbose(`Create client ${parseWebsocket.clientId} new subscription: ${request.requestId}`);
logger.verbose(
`Create client ${parseWebsocket.clientId} new subscription: ${
request.requestId
}`
);
logger.verbose('Current client number: %d', this.clients.size);
runLiveQueryEventHandlers({
event: 'subscribe',
clients: this.clients.size,
subscriptions: this.subscriptions.size
subscriptions: this.subscriptions.size,
});
}
@@ -520,27 +591,54 @@ class ParseLiveQueryServer {
this._handleSubscribe(parseWebsocket, request);
}
_handleUnsubscribe(parseWebsocket: any, request: any, notifyClient: bool = true): any {
_handleUnsubscribe(
parseWebsocket: any,
request: any,
notifyClient: boolean = true
): any {
// If we can not find this client, return error to client
if (!parseWebsocket.hasOwnProperty('clientId')) {
Client.pushError(parseWebsocket, 2, 'Can not find this client, make sure you connect to server before unsubscribing');
logger.error('Can not find this client, make sure you connect to server before unsubscribing');
Client.pushError(
parseWebsocket,
2,
'Can not find this client, make sure you connect to server before unsubscribing'
);
logger.error(
'Can not find this client, make sure you connect to server before unsubscribing'
);
return;
}
const requestId = request.requestId;
const client = this.clients.get(parseWebsocket.clientId);
if (typeof client === 'undefined') {
Client.pushError(parseWebsocket, 2, 'Cannot find client with clientId ' + parseWebsocket.clientId +
'. Make sure you connect to live query server before unsubscribing.');
Client.pushError(
parseWebsocket,
2,
'Cannot find client with clientId ' +
parseWebsocket.clientId +
'. Make sure you connect to live query server before unsubscribing.'
);
logger.error('Can not find this client ' + parseWebsocket.clientId);
return;
}
const subscriptionInfo = client.getSubscriptionInfo(requestId);
if (typeof subscriptionInfo === 'undefined') {
Client.pushError(parseWebsocket, 2, 'Cannot find subscription with clientId ' + parseWebsocket.clientId +
' subscriptionId ' + requestId + '. Make sure you subscribe to live query server before unsubscribing.');
logger.error('Can not find subscription with clientId ' + parseWebsocket.clientId + ' subscriptionId ' + requestId);
Client.pushError(
parseWebsocket,
2,
'Cannot find subscription with clientId ' +
parseWebsocket.clientId +
' subscriptionId ' +
requestId +
'. Make sure you subscribe to live query server before unsubscribing.'
);
logger.error(
'Can not find subscription with clientId ' +
parseWebsocket.clientId +
' subscriptionId ' +
requestId
);
return;
}
@@ -562,7 +660,7 @@ class ParseLiveQueryServer {
runLiveQueryEventHandlers({
event: 'unsubscribe',
clients: this.clients.size,
subscriptions: this.subscriptions.size
subscriptions: this.subscriptions.size,
});
if (!notifyClient) {
@@ -571,10 +669,12 @@ class ParseLiveQueryServer {
client.pushUnsubscribe(request.requestId);
logger.verbose(`Delete client: ${parseWebsocket.clientId} | subscription: ${request.requestId}`);
logger.verbose(
`Delete client: ${parseWebsocket.clientId} | subscription: ${
request.requestId
}`
);
}
}
export {
ParseLiveQueryServer
}
export { ParseLiveQueryServer };

View File

@@ -1,11 +1,7 @@
import { loadAdapter } from '../Adapters/AdapterLoader';
import {
EventEmitterPubSub
} from '../Adapters/PubSub/EventEmitterPubSub';
import { EventEmitterPubSub } from '../Adapters/PubSub/EventEmitterPubSub';
import {
RedisPubSub
} from '../Adapters/PubSub/RedisPubSub';
import { RedisPubSub } from '../Adapters/PubSub/RedisPubSub';
const ParsePubSub = {};
@@ -18,26 +14,32 @@ ParsePubSub.createPublisher = function(config: any): any {
if (useRedis(config)) {
return RedisPubSub.createPublisher(config);
} else {
const adapter = loadAdapter(config.pubSubAdapter, EventEmitterPubSub, config)
const adapter = loadAdapter(
config.pubSubAdapter,
EventEmitterPubSub,
config
);
if (typeof adapter.createPublisher !== 'function') {
throw 'pubSubAdapter should have createPublisher()';
}
return adapter.createPublisher(config);
}
}
};
ParsePubSub.createSubscriber = function(config: any): void {
if (useRedis(config)) {
return RedisPubSub.createSubscriber(config);
} else {
const adapter = loadAdapter(config.pubSubAdapter, EventEmitterPubSub, config)
const adapter = loadAdapter(
config.pubSubAdapter,
EventEmitterPubSub,
config
);
if (typeof adapter.createSubscriber !== 'function') {
throw 'pubSubAdapter should have createSubscriber()';
}
return adapter.createSubscriber(config);
}
}
};
export {
ParsePubSub
}
export { ParsePubSub };

View File

@@ -4,21 +4,25 @@ const typeMap = new Map([['disconnect', 'close']]);
const getWS = function() {
try {
return require('uws');
} catch(e) {
} catch (e) {
return require('ws');
}
}
};
export class ParseWebSocketServer {
server: Object;
constructor(server: any, onConnect: Function, websocketTimeout: number = 10 * 1000) {
constructor(
server: any,
onConnect: Function,
websocketTimeout: number = 10 * 1000
) {
const WebSocketServer = getWS().Server;
const wss = new WebSocketServer({ server: server });
wss.on('listening', () => {
logger.info('Parse LiveQuery Server starts running');
});
wss.on('connection', (ws) => {
wss.on('connection', ws => {
onConnect(new ParseWebSocket(ws));
// Send ping to client periodically
const pingIntervalId = setInterval(() => {

View File

@@ -55,8 +55,8 @@ function queryHash(query) {
if (query instanceof Parse.Query) {
query = {
className: query.className,
where: query._where
}
where: query._where,
};
}
var where = flattenOrQueries(query.where || {});
var columns = [];
@@ -99,8 +99,10 @@ function contains(haystack: Array, needle: any): boolean {
if (typeof ptr === 'string' && ptr === needle.objectId) {
return true;
}
if (ptr.className === needle.className &&
ptr.objectId === needle.objectId) {
if (
ptr.className === needle.className &&
ptr.objectId === needle.objectId
) {
return true;
}
}
@@ -117,7 +119,7 @@ function contains(haystack: Array, needle: any): boolean {
function matchesQuery(object: any, query: any): boolean {
if (query instanceof Parse.Query) {
var className =
(object.id instanceof Id) ? object.id.className : object.className;
object.id instanceof Id ? object.id.className : object.className;
if (className !== query.className) {
return false;
}
@@ -144,7 +146,6 @@ function equalObjectsGeneric(obj, compareTo, eqlFn) {
return eqlFn(obj, compareTo);
}
/**
* Determines whether an object matches a single key's constraints
*/
@@ -152,12 +153,16 @@ function matchesKeyConstraints(object, key, constraints) {
if (constraints === null) {
return false;
}
if(key.indexOf(".") >= 0){
if (key.indexOf('.') >= 0) {
// Key references a subobject
var keyComponents = key.split(".");
var keyComponents = key.split('.');
var subObjectKey = keyComponents[0];
var keyRemainder = keyComponents.slice(1).join(".");
return matchesKeyConstraints(object[subObjectKey] || {}, keyRemainder, constraints);
var keyRemainder = keyComponents.slice(1).join('.');
return matchesKeyConstraints(
object[subObjectKey] || {},
keyRemainder,
constraints
);
}
var i;
if (key === '$or') {
@@ -191,7 +196,11 @@ function matchesKeyConstraints(object, key, constraints) {
});
}
return equalObjectsGeneric(object[key], Parse._decode(key, constraints), equalObjects);
return equalObjectsGeneric(
object[key],
Parse._decode(key, constraints),
equalObjects
);
}
// More complex cases
for (var condition in constraints) {
@@ -200,124 +209,131 @@ function matchesKeyConstraints(object, key, constraints) {
compareTo = Parse._decode(key, compareTo);
}
switch (condition) {
case '$lt':
if (object[key] >= compareTo) {
return false;
}
break;
case '$lte':
if (object[key] > compareTo) {
return false;
}
break;
case '$gt':
if (object[key] <= compareTo) {
return false;
}
break;
case '$gte':
if (object[key] < compareTo) {
return false;
}
break;
case '$ne':
if (equalObjects(object[key], compareTo)) {
return false;
}
break;
case '$in':
if (!contains(compareTo, object[key])) {
return false;
}
break;
case '$nin':
if (contains(compareTo, object[key])) {
return false;
}
break;
case '$all':
for (i = 0; i < compareTo.length; i++) {
if (object[key].indexOf(compareTo[i]) < 0) {
case '$lt':
if (object[key] >= compareTo) {
return false;
}
break;
case '$lte':
if (object[key] > compareTo) {
return false;
}
break;
case '$gt':
if (object[key] <= compareTo) {
return false;
}
break;
case '$gte':
if (object[key] < compareTo) {
return false;
}
break;
case '$ne':
if (equalObjects(object[key], compareTo)) {
return false;
}
break;
case '$in':
if (!contains(compareTo, object[key])) {
return false;
}
break;
case '$nin':
if (contains(compareTo, object[key])) {
return false;
}
break;
case '$all':
for (i = 0; i < compareTo.length; i++) {
if (object[key].indexOf(compareTo[i]) < 0) {
return false;
}
}
break;
case '$exists': {
const propertyExists = typeof object[key] !== 'undefined';
const existenceIsRequired = constraints['$exists'];
if (typeof constraints['$exists'] !== 'boolean') {
// The SDK will never submit a non-boolean for $exists, but if someone
// tries to submit a non-boolean for $exits outside the SDKs, just ignore it.
break;
}
if (
(!propertyExists && existenceIsRequired) ||
(propertyExists && !existenceIsRequired)
) {
return false;
}
}
break;
case '$exists': {
const propertyExists = typeof object[key] !== 'undefined';
const existenceIsRequired = constraints['$exists'];
if (typeof constraints['$exists'] !== 'boolean') {
// The SDK will never submit a non-boolean for $exists, but if someone
// tries to submit a non-boolean for $exits outside the SDKs, just ignore it.
break;
}
if ((!propertyExists && existenceIsRequired) || (propertyExists && !existenceIsRequired)) {
return false;
}
break;
}
case '$regex':
if (typeof compareTo === 'object') {
return compareTo.test(object[key]);
}
// JS doesn't support perl-style escaping
var expString = '';
var escapeEnd = -2;
var escapeStart = compareTo.indexOf('\\Q');
while (escapeStart > -1) {
// Add the unescaped portion
expString += compareTo.substring(escapeEnd + 2, escapeStart);
escapeEnd = compareTo.indexOf('\\E', escapeStart);
if (escapeEnd > -1) {
expString += compareTo.substring(escapeStart + 2, escapeEnd)
.replace(/\\\\\\\\E/g, '\\E').replace(/\W/g, '\\$&');
case '$regex':
if (typeof compareTo === 'object') {
return compareTo.test(object[key]);
}
// JS doesn't support perl-style escaping
var expString = '';
var escapeEnd = -2;
var escapeStart = compareTo.indexOf('\\Q');
while (escapeStart > -1) {
// Add the unescaped portion
expString += compareTo.substring(escapeEnd + 2, escapeStart);
escapeEnd = compareTo.indexOf('\\E', escapeStart);
if (escapeEnd > -1) {
expString += compareTo
.substring(escapeStart + 2, escapeEnd)
.replace(/\\\\\\\\E/g, '\\E')
.replace(/\W/g, '\\$&');
}
escapeStart = compareTo.indexOf('\\Q', escapeEnd);
}
expString += compareTo.substring(Math.max(escapeStart, escapeEnd + 2));
var exp = new RegExp(expString, constraints.$options || '');
if (!exp.test(object[key])) {
return false;
}
break;
case '$nearSphere':
if (!compareTo || !object[key]) {
return false;
}
var distance = compareTo.radiansTo(object[key]);
var max = constraints.$maxDistance || Infinity;
return distance <= max;
case '$within':
if (!compareTo || !object[key]) {
return false;
}
var southWest = compareTo.$box[0];
var northEast = compareTo.$box[1];
if (southWest.latitude > northEast.latitude ||
southWest.longitude > northEast.longitude) {
// Invalid box, crosses the date line
return false;
}
return (
object[key].latitude > southWest.latitude &&
escapeStart = compareTo.indexOf('\\Q', escapeEnd);
}
expString += compareTo.substring(Math.max(escapeStart, escapeEnd + 2));
var exp = new RegExp(expString, constraints.$options || '');
if (!exp.test(object[key])) {
return false;
}
break;
case '$nearSphere':
if (!compareTo || !object[key]) {
return false;
}
var distance = compareTo.radiansTo(object[key]);
var max = constraints.$maxDistance || Infinity;
return distance <= max;
case '$within':
if (!compareTo || !object[key]) {
return false;
}
var southWest = compareTo.$box[0];
var northEast = compareTo.$box[1];
if (
southWest.latitude > northEast.latitude ||
southWest.longitude > northEast.longitude
) {
// Invalid box, crosses the date line
return false;
}
return (
object[key].latitude > southWest.latitude &&
object[key].latitude < northEast.latitude &&
object[key].longitude > southWest.longitude &&
object[key].longitude < northEast.longitude
);
case '$options':
// Not a query type, but a way to add options to $regex. Ignore and
// avoid the default
break;
case '$maxDistance':
// Not a query type, but a way to add a cap to $nearSphere. Ignore and
// avoid the default
break;
case '$select':
return false;
case '$dontSelect':
return false;
default:
return false;
);
case '$options':
// Not a query type, but a way to add options to $regex. Ignore and
// avoid the default
break;
case '$maxDistance':
// Not a query type, but a way to add a cap to $nearSphere. Ignore and
// avoid the default
break;
case '$select':
return false;
case '$dontSelect':
return false;
default:
return false;
}
}
return true;
@@ -325,7 +341,7 @@ function matchesKeyConstraints(object, key, constraints) {
var QueryTools = {
queryHash: queryHash,
matchesQuery: matchesQuery
matchesQuery: matchesQuery,
};
module.exports = QueryTools;

View File

@@ -1,141 +1,141 @@
const general = {
'title': 'General request schema',
'type': 'object',
'properties': {
'op': {
'type': 'string',
'enum': ['connect', 'subscribe', 'unsubscribe', 'update']
title: 'General request schema',
type: 'object',
properties: {
op: {
type: 'string',
enum: ['connect', 'subscribe', 'unsubscribe', 'update'],
},
},
'required': ['op']
required: ['op'],
};
const connect = {
'title': 'Connect operation schema',
'type': 'object',
'properties': {
'op': 'connect',
'applicationId': {
'type': 'string'
const connect = {
title: 'Connect operation schema',
type: 'object',
properties: {
op: 'connect',
applicationId: {
type: 'string',
},
'javascriptKey': {
type: 'string'
javascriptKey: {
type: 'string',
},
'masterKey': {
type: 'string'
masterKey: {
type: 'string',
},
'clientKey': {
type: 'string'
clientKey: {
type: 'string',
},
'windowsKey': {
type: 'string'
windowsKey: {
type: 'string',
},
'restAPIKey': {
'type': 'string'
restAPIKey: {
type: 'string',
},
sessionToken: {
type: 'string',
},
'sessionToken': {
'type': 'string'
}
},
'required': ['op', 'applicationId'],
"additionalProperties": false
required: ['op', 'applicationId'],
additionalProperties: false,
};
const subscribe = {
'title': 'Subscribe operation schema',
'type': 'object',
'properties': {
'op': 'subscribe',
'requestId': {
'type': 'number'
title: 'Subscribe operation schema',
type: 'object',
properties: {
op: 'subscribe',
requestId: {
type: 'number',
},
'query': {
'title': 'Query field schema',
'type': 'object',
'properties': {
'className': {
'type': 'string'
query: {
title: 'Query field schema',
type: 'object',
properties: {
className: {
type: 'string',
},
'where': {
'type': 'object'
where: {
type: 'object',
},
'fields': {
"type": "array",
"items": {
"type": "string"
fields: {
type: 'array',
items: {
type: 'string',
},
"minItems": 1,
"uniqueItems": true
}
minItems: 1,
uniqueItems: true,
},
},
'required': ['where', 'className'],
'additionalProperties': false
required: ['where', 'className'],
additionalProperties: false,
},
sessionToken: {
type: 'string',
},
'sessionToken': {
'type': 'string'
}
},
'required': ['op', 'requestId', 'query'],
'additionalProperties': false
required: ['op', 'requestId', 'query'],
additionalProperties: false,
};
const update = {
'title': 'Update operation schema',
'type': 'object',
'properties': {
'op': 'update',
'requestId': {
'type': 'number'
title: 'Update operation schema',
type: 'object',
properties: {
op: 'update',
requestId: {
type: 'number',
},
'query': {
'title': 'Query field schema',
'type': 'object',
'properties': {
'className': {
'type': 'string'
query: {
title: 'Query field schema',
type: 'object',
properties: {
className: {
type: 'string',
},
'where': {
'type': 'object'
where: {
type: 'object',
},
'fields': {
"type": "array",
"items": {
"type": "string"
fields: {
type: 'array',
items: {
type: 'string',
},
"minItems": 1,
"uniqueItems": true
}
minItems: 1,
uniqueItems: true,
},
},
'required': ['where', 'className'],
'additionalProperties': false
required: ['where', 'className'],
additionalProperties: false,
},
sessionToken: {
type: 'string',
},
'sessionToken': {
'type': 'string'
}
},
'required': ['op', 'requestId', 'query'],
'additionalProperties': false
required: ['op', 'requestId', 'query'],
additionalProperties: false,
};
const unsubscribe = {
'title': 'Unsubscribe operation schema',
'type': 'object',
'properties': {
'op': 'unsubscribe',
'requestId': {
'type': 'number'
}
title: 'Unsubscribe operation schema',
type: 'object',
properties: {
op: 'unsubscribe',
requestId: {
type: 'number',
},
},
'required': ['op', 'requestId'],
"additionalProperties": false
}
required: ['op', 'requestId'],
additionalProperties: false,
};
const RequestSchema = {
'general': general,
'connect': connect,
'subscribe': subscribe,
'update': update,
'unsubscribe': unsubscribe
}
general: general,
connect: connect,
subscribe: subscribe,
update: update,
unsubscribe: unsubscribe,
};
export default RequestSchema;

View File

@@ -2,24 +2,27 @@ import Parse from 'parse/node';
import LRU from 'lru-cache';
import logger from '../logger';
function userForSessionToken(sessionToken){
var q = new Parse.Query("_Session");
q.equalTo("sessionToken", sessionToken);
return q.first({useMasterKey:true}).then(function(session){
if(!session){
return Promise.reject("No session found for session token");
function userForSessionToken(sessionToken) {
var q = new Parse.Query('_Session');
q.equalTo('sessionToken', sessionToken);
return q.first({ useMasterKey: true }).then(function(session) {
if (!session) {
return Promise.reject('No session found for session token');
}
return session.get("user");
return session.get('user');
});
}
class SessionTokenCache {
cache: Object;
constructor(timeout: number = 30 * 24 * 60 * 60 * 1000, maxSize: number = 10000) {
constructor(
timeout: number = 30 * 24 * 60 * 60 * 1000,
maxSize: number = 10000
) {
this.cache = new LRU({
max: maxSize,
maxAge: timeout
maxAge: timeout,
});
}
@@ -29,21 +32,34 @@ class SessionTokenCache {
}
const userId = this.cache.get(sessionToken);
if (userId) {
logger.verbose('Fetch userId %s of sessionToken %s from Cache', userId, sessionToken);
logger.verbose(
'Fetch userId %s of sessionToken %s from Cache',
userId,
sessionToken
);
return Promise.resolve(userId);
}
return userForSessionToken(sessionToken).then((user) => {
logger.verbose('Fetch userId %s of sessionToken %s from Parse', user.id, sessionToken);
const userId = user.id;
this.cache.set(sessionToken, userId);
return Promise.resolve(userId);
}, (error) => {
logger.error('Can not fetch userId for sessionToken %j, error %j', sessionToken, error);
return Promise.reject(error);
});
return userForSessionToken(sessionToken).then(
user => {
logger.verbose(
'Fetch userId %s of sessionToken %s from Parse',
user.id,
sessionToken
);
const userId = user.id;
this.cache.set(sessionToken, userId);
return Promise.resolve(userId);
},
error => {
logger.error(
'Can not fetch userId for sessionToken %j, error %j',
sessionToken,
error
);
return Promise.reject(error);
}
);
}
}
export {
SessionTokenCache
}
export { SessionTokenCache };

View File

@@ -34,7 +34,11 @@ class Subscription {
const index = requestIds.indexOf(requestId);
if (index < 0) {
logger.error('Can not find client %d subscription %d to delete', clientId, requestId);
logger.error(
'Can not find client %d subscription %d to delete',
clientId,
requestId
);
return;
}
requestIds.splice(index, 1);
@@ -49,6 +53,4 @@ class Subscription {
}
}
export {
Subscription
}
export { Subscription };

View File

@@ -9,14 +9,14 @@ function equalObjects(a, b) {
return false;
}
if (typeof a !== 'object') {
return (a === b);
return a === b;
}
if (a === b) {
return true;
}
if (toString.call(a) === '[object Date]') {
if (toString.call(b) === '[object Date]') {
return (+a === +b);
return +a === +b;
}
return false;
}