Skip to content

Commit 728c43e

Browse files
authored
Support query.cancel() (#1003)
* Support: query.cancel() Closes: #997 Taken from Android API [cancel](http://parseplatform.org/Parse-SDK-Android/api/com/parse/ParseQuery.html#cancel--) Can be improved in the future. * fix tests * Add xmlhttprequest note * remove tests
1 parent 566f29a commit 728c43e

File tree

2 files changed

+91
-43
lines changed

2 files changed

+91
-43
lines changed

src/ParseQuery.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ class ParseQuery {
232232
_queriesLocalDatastore: boolean;
233233
_localDatastorePinName: any;
234234
_extraOptions: { [key: string]: mixed };
235+
_xhrRequest: any;
235236

236237
/**
237238
* @param {(String|Parse.Object)} objectClass An instance of a subclass of Parse.Object, or a Parse className string.
@@ -270,6 +271,10 @@ class ParseQuery {
270271
this._queriesLocalDatastore = false;
271272
this._localDatastorePinName = null;
272273
this._extraOptions = {};
274+
this._xhrRequest = {
275+
task: null,
276+
onchange: () => {},
277+
}
273278
}
274279

275280
/**
@@ -596,6 +601,7 @@ class ParseQuery {
596601
if (options.hasOwnProperty('sessionToken')) {
597602
findOptions.sessionToken = options.sessionToken;
598603
}
604+
this._setRequestTask(findOptions);
599605

600606
const controller = CoreManager.getQueryController();
601607

@@ -664,6 +670,7 @@ class ParseQuery {
664670
if (options.hasOwnProperty('sessionToken')) {
665671
findOptions.sessionToken = options.sessionToken;
666672
}
673+
this._setRequestTask(findOptions);
667674

668675
const controller = CoreManager.getQueryController();
669676

@@ -701,12 +708,13 @@ class ParseQuery {
701708
if (options.hasOwnProperty('sessionToken')) {
702709
distinctOptions.sessionToken = options.sessionToken;
703710
}
711+
this._setRequestTask(distinctOptions);
712+
704713
const controller = CoreManager.getQueryController();
705714
const params = {
706715
distinct: key,
707716
where: this._where
708717
};
709-
710718
return controller.aggregate(
711719
this.className,
712720
params,
@@ -736,6 +744,8 @@ class ParseQuery {
736744
if (options.hasOwnProperty('sessionToken')) {
737745
aggregateOptions.sessionToken = options.sessionToken;
738746
}
747+
this._setRequestTask(aggregateOptions);
748+
739749
const controller = CoreManager.getQueryController();
740750

741751
if (!Array.isArray(pipeline) && typeof pipeline !== 'object') {
@@ -779,6 +789,7 @@ class ParseQuery {
779789
if (options.hasOwnProperty('sessionToken')) {
780790
findOptions.sessionToken = options.sessionToken;
781791
}
792+
this._setRequestTask(findOptions);
782793

783794
const controller = CoreManager.getQueryController();
784795

@@ -1817,6 +1828,27 @@ class ParseQuery {
18171828
}
18181829
return this;
18191830
}
1831+
1832+
/**
1833+
* Cancels the current network request (if any is running).
1834+
* Note: Support varies based on xmlhttprequest module used. (Will support browser)
1835+
*
1836+
* @return {Parse.Query} Returns the query, so you can chain this call.
1837+
*/
1838+
cancel(): ParseQuery {
1839+
if (this._xhrRequest.task && typeof this._xhrRequest.task.abort === 'function') {
1840+
this._xhrRequest.task.abort();
1841+
}
1842+
this._xhrRequest.task = null;
1843+
return this;
1844+
}
1845+
1846+
_setRequestTask(options) {
1847+
options.requestTask = (task) => {
1848+
this._xhrRequest.task = task;
1849+
this._xhrRequest.onchange();
1850+
};
1851+
}
18201852
}
18211853

18221854
const DefaultController = {

src/__tests__/ParseQuery-test.js

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ describe('ParseQuery', () => {
11401140
size: 'small'
11411141
}
11421142
});
1143-
expect(options).toEqual({});
1143+
expect(options.requestTask).toBeDefined();
11441144
return Promise.resolve({
11451145
results: [
11461146
{ objectId: 'I1', size: 'small', name: 'Product 3' }
@@ -1173,10 +1173,8 @@ describe('ParseQuery', () => {
11731173
size: 'small'
11741174
}
11751175
});
1176-
expect(options).toEqual({
1177-
useMasterKey: true,
1178-
sessionToken: '1234'
1179-
});
1176+
expect(options.useMasterKey).toEqual(true);
1177+
expect(options.sessionToken).toEqual('1234');
11801178
return Promise.resolve({
11811179
results: []
11821180
});
@@ -1204,7 +1202,7 @@ describe('ParseQuery', () => {
12041202
objectId: 'I27'
12051203
}
12061204
});
1207-
expect(options).toEqual({});
1205+
expect(options.requestTask).toBeDefined();
12081206
return Promise.resolve({
12091207
results: [
12101208
{ objectId: 'I27', size: 'large', name: 'Product 27' }
@@ -1237,7 +1235,7 @@ describe('ParseQuery', () => {
12371235
objectId: 'I28'
12381236
}
12391237
});
1240-
expect(options).toEqual({});
1238+
expect(options.requestTask).toBeDefined();
12411239
return Promise.resolve({
12421240
results: []
12431241
});
@@ -1267,10 +1265,8 @@ describe('ParseQuery', () => {
12671265
objectId: 'I27'
12681266
}
12691267
});
1270-
expect(options).toEqual({
1271-
useMasterKey: true,
1272-
sessionToken: '1234'
1273-
});
1268+
expect(options.useMasterKey).toEqual(true);
1269+
expect(options.sessionToken).toEqual('1234');
12741270
return Promise.resolve({
12751271
results: [
12761272
{ objectId: 'I27', size: 'large', name: 'Product 27' }
@@ -1300,7 +1296,7 @@ describe('ParseQuery', () => {
13001296
size: 'small'
13011297
}
13021298
});
1303-
expect(options).toEqual({});
1299+
expect(options.requestTask).toBeDefined();
13041300
return Promise.resolve({
13051301
results: [],
13061302
count: 145
@@ -1327,10 +1323,8 @@ describe('ParseQuery', () => {
13271323
size: 'small'
13281324
}
13291325
});
1330-
expect(options).toEqual({
1331-
useMasterKey: true,
1332-
sessionToken: '1234'
1333-
});
1326+
expect(options.useMasterKey).toEqual(true);
1327+
expect(options.sessionToken).toEqual('1234');
13341328
return Promise.resolve({
13351329
results: [],
13361330
count: 145
@@ -1368,7 +1362,7 @@ describe('ParseQuery', () => {
13681362
includeReadPreference: 'SECONDARY',
13691363
subqueryReadPreference: 'SECONDARY_PREFERRED',
13701364
});
1371-
expect(options).toEqual({});
1365+
expect(options.requestTask).toBeDefined();
13721366
return Promise.resolve({
13731367
results: [
13741368
{ objectId: 'I55', size: 'medium', name: 'Product 55' },
@@ -1414,10 +1408,8 @@ describe('ParseQuery', () => {
14141408
}
14151409
}
14161410
});
1417-
expect(options).toEqual({
1418-
useMasterKey: true,
1419-
sessionToken: '1234'
1420-
});
1411+
expect(options.useMasterKey).toEqual(true);
1412+
expect(options.sessionToken).toEqual('1234');
14211413
return Promise.resolve({
14221414
results: []
14231415
});
@@ -1445,10 +1437,8 @@ describe('ParseQuery', () => {
14451437
where: {},
14461438
count: 1
14471439
});
1448-
expect(options).toEqual({
1449-
useMasterKey: true,
1450-
sessionToken: '1234'
1451-
});
1440+
expect(options.useMasterKey).toEqual(true);
1441+
expect(options.sessionToken).toEqual('1234');
14521442
return Promise.resolve({
14531443
results:[
14541444
{ objectId: '1', name: 'Product 55' },
@@ -1502,7 +1492,7 @@ describe('ParseQuery', () => {
15021492
valid: true
15031493
}
15041494
});
1505-
expect(options).toEqual({});
1495+
expect(options.requestTask).toBeDefined();
15061496
return Promise.resolve({
15071497
results: [
15081498
{ objectId: 'I55', size: 'medium', name: 'Product 55' },
@@ -1549,10 +1539,8 @@ describe('ParseQuery', () => {
15491539
valid: true
15501540
}
15511541
});
1552-
expect(options).toEqual({
1553-
useMasterKey: true,
1554-
sessionToken: '1234'
1555-
});
1542+
expect(options.useMasterKey).toEqual(true);
1543+
expect(options.sessionToken).toEqual('1234');
15561544
return Promise.resolve({
15571545
results: [
15581546
{ objectId: 'I55', size: 'medium', name: 'Product 55' },
@@ -1964,7 +1952,8 @@ describe('ParseQuery', () => {
19641952
size: 'small'
19651953
}
19661954
});
1967-
expect(options).toEqual({ useMasterKey: true });
1955+
expect(options.useMasterKey).toEqual(true);
1956+
expect(options.requestTask).toBeDefined();
19681957
return Promise.resolve({
19691958
results: ['L'],
19701959
});
@@ -1989,10 +1978,9 @@ describe('ParseQuery', () => {
19891978
size: 'small'
19901979
}
19911980
});
1992-
expect(options).toEqual({
1993-
useMasterKey: true,
1994-
sessionToken: '1234'
1995-
});
1981+
expect(options.useMasterKey).toEqual(true);
1982+
expect(options.sessionToken).toEqual('1234');
1983+
expect(options.requestTask).toBeDefined();
19961984
return Promise.resolve({
19971985
results: ['L']
19981986
});
@@ -2020,7 +2008,8 @@ describe('ParseQuery', () => {
20202008
expect(params).toEqual({
20212009
pipeline: [{ group: { objectId: '$name' } }]
20222010
});
2023-
expect(options).toEqual({ useMasterKey: true });
2011+
expect(options.useMasterKey).toEqual(true);
2012+
expect(options.requestTask).toBeDefined();
20242013
return Promise.resolve({
20252014
results: [],
20262015
});
@@ -2045,7 +2034,8 @@ describe('ParseQuery', () => {
20452034
expect(params).toEqual({
20462035
pipeline: { group: { objectId: '$name' } }
20472036
});
2048-
expect(options).toEqual({ useMasterKey: true });
2037+
expect(options.useMasterKey).toEqual(true);
2038+
expect(options.requestTask).toBeDefined();
20492039
return Promise.resolve({
20502040
results: [],
20512041
});
@@ -2068,7 +2058,8 @@ describe('ParseQuery', () => {
20682058
expect(params).toEqual({
20692059
group: { objectId: '$name' }
20702060
});
2071-
expect(options).toEqual({ useMasterKey: true });
2061+
expect(options.useMasterKey).toEqual(true);
2062+
expect(options.requestTask).toBeDefined();
20722063
return Promise.resolve({
20732064
results: [],
20742065
});
@@ -2094,10 +2085,8 @@ describe('ParseQuery', () => {
20942085
expect(params).toEqual({
20952086
pipeline: [{ group: { objectId: '$name' } }]
20962087
});
2097-
expect(options).toEqual({
2098-
useMasterKey: true,
2099-
sessionToken: '1234'
2100-
});
2088+
expect(options.useMasterKey).toEqual(true);
2089+
expect(options.sessionToken).toEqual('1234');
21012090
return Promise.resolve({
21022091
results: []
21032092
});
@@ -2113,6 +2102,33 @@ describe('ParseQuery', () => {
21132102
});
21142103
});
21152104

2105+
it('can cancel query', async () => {
2106+
const mockRequestTask = {
2107+
abort: () => {},
2108+
};
2109+
2110+
CoreManager.setQueryController({
2111+
find: function(name, params, options) {
2112+
options.requestTask(mockRequestTask);
2113+
return Promise.resolve({
2114+
results: []
2115+
});
2116+
},
2117+
aggregate: () => {},
2118+
});
2119+
const query = new ParseQuery('TestCancel');
2120+
2121+
jest.spyOn(mockRequestTask, 'abort');
2122+
query.cancel();
2123+
expect(mockRequestTask.abort).toHaveBeenCalledTimes(0);
2124+
2125+
await query.find();
2126+
2127+
expect(query._xhrRequest.task).toEqual(mockRequestTask);
2128+
query.cancel();
2129+
expect(mockRequestTask.abort).toHaveBeenCalledTimes(1);
2130+
});
2131+
21162132
it('selecting sub-objects does not inject objects when sub-object does not exist', (done) => {
21172133
jest.dontMock("../ParseObject");
21182134
jest.resetModules();

0 commit comments

Comments
 (0)