@@ -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 };
|
||||
|
||||
Reference in New Issue
Block a user