Futzing with read preference (#3963)

* allow setting readpreference when using rest api.

* take out partially complete unit test.

* oops. nit

* Include read preference option for find directly from api and adding few more tests

* Adding catch for all tests

* Keep same check for get and find

* Turn read preference case insensitive

* Includes and subqueries read preferences through API

* Fixing bugs regarding changes that were done in master branch during the last year

* Changing behavior to make includeReadPreference and subqueryReadPreference to follow readPreference by default
This commit is contained in:
Antonio Davi Macedo Coelho de Castro
2019-05-14 12:58:02 -07:00
committed by GitHub
parent 893f1d376e
commit afa74d655d
6 changed files with 915 additions and 333 deletions

View File

@@ -41,16 +41,12 @@ describe('GridFSBucket and GridStore interop', () => {
expect(gfsResult.toString('utf8')).toBe(twoMegabytesFile);
});
it(
'properly deletes a file from GridFS',
async () => {
it('properly deletes a file from GridFS', async () => {
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
await gfsAdapter.createFile('myFileName', 'a simple file');
await gfsAdapter.deleteFile('myFileName');
await expectMissingFile(gfsAdapter, 'myFileName');
},
1000000
);
}, 1000000);
it('properly overrides files', async () => {
const gfsAdapter = new GridFSBucketAdapter(databaseURI);

View File

@@ -108,7 +108,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
query.find().then(results => {
query
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
@@ -124,8 +126,50 @@ describe_only_db('mongo')('Read preference option', () => {
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
})
.catch(done.fail);
});
});
it('should check read preference as case insensitive', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
Parse.Object.saveAll([obj0, obj1]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'sEcOnDarY';
});
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
query
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
})
.catch(done.fail);
});
});
it('should change read preference in the beforeFind trigger even changing query', done => {
@@ -147,7 +191,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
query.find().then(results => {
query
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(true);
@@ -163,7 +209,8 @@ describe_only_db('mongo')('Read preference option', () => {
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
});
})
.catch(done.fail);
});
});
@@ -189,7 +236,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
query.find().then(results => {
query
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(true);
@@ -205,7 +254,8 @@ describe_only_db('mongo')('Read preference option', () => {
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
});
})
.catch(done.fail);
});
});
@@ -231,7 +281,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
query.find().then(results => {
query
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(true);
@@ -247,7 +299,8 @@ describe_only_db('mongo')('Read preference option', () => {
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
});
})
.catch(done.fail);
});
});
@@ -269,7 +322,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
query.find().then(results => {
query
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
@@ -287,7 +342,8 @@ describe_only_db('mongo')('Read preference option', () => {
);
done();
});
})
.catch(done.fail);
});
});
@@ -309,7 +365,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
query.find().then(results => {
query
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
@@ -327,7 +385,8 @@ describe_only_db('mongo')('Read preference option', () => {
);
done();
});
})
.catch(done.fail);
});
});
@@ -349,7 +408,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
query.find().then(results => {
query
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
@@ -365,7 +426,8 @@ describe_only_db('mongo')('Read preference option', () => {
expect(myObjectReadPreference).toEqual(ReadPreference.NEAREST);
done();
});
})
.catch(done.fail);
});
});
@@ -386,7 +448,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query = new Parse.Query('MyObject');
query.get(obj0.id).then(result => {
query
.get(obj0.id)
.then(result => {
expect(result.get('boolKey')).toBe(false);
let myObjectReadPreference = null;
@@ -401,7 +465,8 @@ describe_only_db('mongo')('Read preference option', () => {
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
});
})
.catch(done.fail);
});
});
@@ -428,7 +493,8 @@ describe_only_db('mongo')('Read preference option', () => {
'X-Parse-REST-API-Key': 'rest',
},
json: true,
}).then(response => {
})
.then(response => {
const body = response.data;
expect(body.boolKey).toBe(false);
@@ -444,8 +510,233 @@ describe_only_db('mongo')('Read preference option', () => {
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
})
.catch(done.fail);
});
});
it('should change read preference for GET directly from API', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
Parse.Object.saveAll([obj0, obj1]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
request({
method: 'GET',
url:
'http://localhost:8378/1/classes/MyObject/' +
obj0.id +
'?readPreference=SECONDARY',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true,
})
.then(response => {
expect(response.data.boolKey).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
})
.catch(done.fail);
});
});
it('should change read preference for GET using API through the beforeFind overriding API option', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
Parse.Object.saveAll([obj0, obj1]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY_PREFERRED';
});
request({
method: 'GET',
url:
'http://localhost:8378/1/classes/MyObject/' +
obj0.id +
'?readPreference=SECONDARY',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true,
})
.then(response => {
expect(response.data.boolKey).toBe(false);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference).toEqual(
ReadPreference.SECONDARY_PREFERRED
);
done();
})
.catch(done.fail);
});
});
it('should change read preference for FIND using API through beforeFind trigger', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
Parse.Object.saveAll([obj0, obj1]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY';
});
request({
method: 'GET',
url: 'http://localhost:8378/1/classes/MyObject/',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true,
})
.then(response => {
expect(response.data.results.length).toEqual(2);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
})
.catch(done.fail);
});
});
it('should change read preference for FIND directly from API', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
Parse.Object.saveAll([obj0, obj1]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
request({
method: 'GET',
url:
'http://localhost:8378/1/classes/MyObject?readPreference=SECONDARY',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true,
})
.then(response => {
expect(response.data.results.length).toEqual(2);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
})
.catch(done.fail);
});
});
it('should change read preference for FIND using API through the beforeFind overriding API option', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject');
obj1.set('boolKey', true);
Parse.Object.saveAll([obj0, obj1]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
Parse.Cloud.beforeFind('MyObject', req => {
req.readPreference = 'SECONDARY_PREFERRED';
});
request({
method: 'GET',
url:
'http://localhost:8378/1/classes/MyObject/?readPreference=SECONDARY',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true,
})
.then(response => {
expect(response.data.results.length).toEqual(2);
let myObjectReadPreference = null;
databaseAdapter.database.serverConfig.cursor.calls
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject') >= 0) {
myObjectReadPreference = call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference).toEqual(
ReadPreference.SECONDARY_PREFERRED
);
done();
})
.catch(done.fail);
});
});
it('should change read preference for count', done => {
@@ -466,7 +757,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query = new Parse.Query('MyObject');
query.equalTo('boolKey', false);
query.count().then(result => {
query
.count()
.then(result => {
expect(result).toBe(1);
let myObjectReadPreference = null;
@@ -481,11 +774,12 @@ describe_only_db('mongo')('Read preference option', () => {
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
done();
});
})
.catch(done.fail);
});
});
it('should find includes in primary by default', done => {
it('should find includes in same replica of readPreference by default', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
@@ -509,7 +803,9 @@ describe_only_db('mongo')('Read preference option', () => {
query.include('myObject1');
query.include('myObject1.myObject0');
query.find().then(results => {
query
.find()
.then(results => {
expect(results.length).toBe(1);
const firstResult = results[0];
expect(firstResult.get('boolKey')).toBe(false);
@@ -528,24 +824,26 @@ describe_only_db('mongo')('Read preference option', () => {
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = true;
expect(call.args[2].readPreference).toBe(null);
myObjectReadPreference0 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = true;
expect(call.args[2].readPreference).toBe(null);
myObjectReadPreference1 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[2].readPreference.preference;
myObjectReadPreference2 =
call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference0).toBe(true);
expect(myObjectReadPreference1).toBe(true);
expect(myObjectReadPreference0).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference1).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY);
done();
});
})
.catch(done.fail);
});
});
@@ -574,7 +872,9 @@ describe_only_db('mongo')('Read preference option', () => {
query.include('myObject1');
query.include('myObject1.myObject0');
query.find().then(results => {
query
.find()
.then(results => {
expect(results.length).toBe(1);
const firstResult = results[0];
expect(firstResult.get('boolKey')).toBe(false);
@@ -593,13 +893,16 @@ describe_only_db('mongo')('Read preference option', () => {
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[2].readPreference.preference;
myObjectReadPreference0 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[2].readPreference.preference;
myObjectReadPreference1 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[2].readPreference.preference;
myObjectReadPreference2 =
call.args[2].readPreference.preference;
}
});
@@ -610,11 +913,147 @@ describe_only_db('mongo')('Read preference option', () => {
);
done();
});
})
.catch(done.fail);
});
});
it('should find subqueries in primary by default', done => {
it('should change includes read preference when finding through API', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
obj1.set('boolKey', true);
obj1.set('myObject0', obj0);
const obj2 = new Parse.Object('MyObject2');
obj2.set('boolKey', false);
obj2.set('myObject1', obj1);
Parse.Object.saveAll([obj0, obj1, obj2]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
request({
method: 'GET',
url:
'http://localhost:8378/1/classes/MyObject2/' +
obj2.id +
'?include=' +
JSON.stringify(['myObject1', 'myObject1.myObject0']) +
'&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true,
})
.then(response => {
const firstResult = response.data;
expect(firstResult.boolKey).toBe(false);
expect(firstResult.myObject1.boolKey).toBe(true);
expect(firstResult.myObject1.myObject0.boolKey).toBe(false);
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject0') >= 0) {
myObjectReadPreference0 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject1') >= 0) {
myObjectReadPreference1 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject2') >= 0) {
myObjectReadPreference2 =
call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference0).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference1).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference2).toEqual(
ReadPreference.SECONDARY_PREFERRED
);
done();
})
.catch(done.fail);
});
});
it('should change includes read preference when getting through API', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
obj1.set('boolKey', true);
obj1.set('myObject0', obj0);
const obj2 = new Parse.Object('MyObject2');
obj2.set('boolKey', false);
obj2.set('myObject1', obj1);
Parse.Object.saveAll([obj0, obj1, obj2]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
request({
method: 'GET',
url:
'http://localhost:8378/1/classes/MyObject2?where=' +
JSON.stringify({ boolKey: false }) +
'&include=' +
JSON.stringify(['myObject1', 'myObject1.myObject0']) +
'&readPreference=SECONDARY_PREFERRED&includeReadPreference=SECONDARY',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true,
})
.then(response => {
expect(response.data.results.length).toBe(1);
const firstResult = response.data.results[0];
expect(firstResult.boolKey).toBe(false);
expect(firstResult.myObject1.boolKey).toBe(true);
expect(firstResult.myObject1.myObject0.boolKey).toBe(false);
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject0') >= 0) {
myObjectReadPreference0 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject1') >= 0) {
myObjectReadPreference1 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject2') >= 0) {
myObjectReadPreference2 =
call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference0).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference1).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference2).toEqual(
ReadPreference.SECONDARY_PREFERRED
);
done();
})
.catch(done.fail);
});
});
it('should find subqueries in same replica of readPreference by default', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
@@ -642,7 +1081,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query2 = new Parse.Query('MyObject2');
query2.matchesQuery('myObject1', query1);
query2.find().then(results => {
query2
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
@@ -653,24 +1094,26 @@ describe_only_db('mongo')('Read preference option', () => {
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = true;
expect(call.args[2].readPreference).toBe(null);
myObjectReadPreference0 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = true;
expect(call.args[2].readPreference).toBe(null);
myObjectReadPreference1 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[2].readPreference.preference;
myObjectReadPreference2 =
call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference0).toBe(true);
expect(myObjectReadPreference1).toBe(true);
expect(myObjectReadPreference0).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference1).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference2).toEqual(ReadPreference.SECONDARY);
done();
});
})
.catch(done.fail);
});
});
@@ -703,7 +1146,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query2 = new Parse.Query('MyObject2');
query2.matchesQuery('myObject1', query1);
query2.find().then(results => {
query2
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
@@ -714,13 +1159,16 @@ describe_only_db('mongo')('Read preference option', () => {
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[2].readPreference.preference;
myObjectReadPreference0 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[2].readPreference.preference;
myObjectReadPreference1 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[2].readPreference.preference;
myObjectReadPreference2 =
call.args[2].readPreference.preference;
}
});
@@ -731,7 +1179,8 @@ describe_only_db('mongo')('Read preference option', () => {
);
done();
});
})
.catch(done.fail);
});
});
@@ -764,7 +1213,9 @@ describe_only_db('mongo')('Read preference option', () => {
const query2 = new Parse.Query('MyObject2');
query2.doesNotMatchQuery('myObject1', query1);
query2.find().then(results => {
query2
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
@@ -775,13 +1226,16 @@ describe_only_db('mongo')('Read preference option', () => {
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[2].readPreference.preference;
myObjectReadPreference0 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[2].readPreference.preference;
myObjectReadPreference1 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[2].readPreference.preference;
myObjectReadPreference2 =
call.args[2].readPreference.preference;
}
});
@@ -792,7 +1246,8 @@ describe_only_db('mongo')('Read preference option', () => {
);
done();
});
})
.catch(done.fail);
});
});
@@ -826,7 +1281,9 @@ describe_only_db('mongo')('Read preference option', () => {
query2.matchesKeyInQuery('boolKey', 'boolKey', query0);
query2.doesNotMatchKeyInQuery('boolKey', 'boolKey', query1);
query2.find().then(results => {
query2
.find()
.then(results => {
expect(results.length).toBe(1);
expect(results[0].get('boolKey')).toBe(false);
@@ -837,13 +1294,16 @@ describe_only_db('mongo')('Read preference option', () => {
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject0') >= 0) {
myObjectReadPreference0 = call.args[2].readPreference.preference;
myObjectReadPreference0 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject1') >= 0) {
myObjectReadPreference1 = call.args[2].readPreference.preference;
myObjectReadPreference1 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject2') >= 0) {
myObjectReadPreference2 = call.args[2].readPreference.preference;
myObjectReadPreference2 =
call.args[2].readPreference.preference;
}
});
@@ -854,7 +1314,90 @@ describe_only_db('mongo')('Read preference option', () => {
);
done();
});
})
.catch(done.fail);
});
});
it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery to find through API', done => {
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
const obj0 = new Parse.Object('MyObject0');
obj0.set('boolKey', false);
const obj1 = new Parse.Object('MyObject1');
obj1.set('boolKey', true);
obj1.set('myObject0', obj0);
const obj2 = new Parse.Object('MyObject2');
obj2.set('boolKey', false);
obj2.set('myObject1', obj1);
Parse.Object.saveAll([obj0, obj1, obj2]).then(() => {
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
const whereString = JSON.stringify({
boolKey: {
$select: {
query: {
className: 'MyObject0',
where: { boolKey: false },
},
key: 'boolKey',
},
$dontSelect: {
query: {
className: 'MyObject1',
where: { boolKey: true },
},
key: 'boolKey',
},
},
});
request({
method: 'GET',
url:
'http://localhost:8378/1/classes/MyObject2/?where=' +
whereString +
'&readPreference=SECONDARY_PREFERRED&subqueryReadPreference=SECONDARY',
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
},
json: true,
})
.then(response => {
expect(response.data.results.length).toBe(1);
expect(response.data.results[0].boolKey).toBe(false);
let myObjectReadPreference0 = null;
let myObjectReadPreference1 = null;
let myObjectReadPreference2 = null;
databaseAdapter.database.serverConfig.cursor.calls
.all()
.forEach(call => {
if (call.args[0].indexOf('MyObject0') >= 0) {
myObjectReadPreference0 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject1') >= 0) {
myObjectReadPreference1 =
call.args[2].readPreference.preference;
}
if (call.args[0].indexOf('MyObject2') >= 0) {
myObjectReadPreference2 =
call.args[2].readPreference.preference;
}
});
expect(myObjectReadPreference0).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference1).toEqual(ReadPreference.SECONDARY);
expect(myObjectReadPreference2).toEqual(
ReadPreference.SECONDARY_PREFERRED
);
done();
})
.catch(done.fail);
});
});
});

View File

@@ -926,6 +926,9 @@ export class MongoStorageAdapter implements StorageAdapter {
}
_parseReadPreference(readPreference: ?string): ?string {
if (readPreference) {
readPreference = readPreference.toUpperCase();
}
switch (readPreference) {
case 'PRIMARY':
readPreference = ReadPreference.PRIMARY;

View File

@@ -14,6 +14,9 @@ const AlwaysSelectedKeys = ['objectId', 'createdAt', 'updatedAt', 'ACL'];
// include
// keys
// redirectClassNameForKey
// readPreference
// includeReadPreference
// subqueryReadPreference
function RestQuery(
config,
auth,
@@ -362,6 +365,8 @@ RestQuery.prototype.replaceInQuery = function() {
if (this.restOptions.subqueryReadPreference) {
additionalOptions.readPreference = this.restOptions.subqueryReadPreference;
additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference;
} else if (this.restOptions.readPreference) {
additionalOptions.readPreference = this.restOptions.readPreference;
}
var subquery = new RestQuery(
@@ -421,6 +426,8 @@ RestQuery.prototype.replaceNotInQuery = function() {
if (this.restOptions.subqueryReadPreference) {
additionalOptions.readPreference = this.restOptions.subqueryReadPreference;
additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference;
} else if (this.restOptions.readPreference) {
additionalOptions.readPreference = this.restOptions.readPreference;
}
var subquery = new RestQuery(
@@ -484,6 +491,8 @@ RestQuery.prototype.replaceSelect = function() {
if (this.restOptions.subqueryReadPreference) {
additionalOptions.readPreference = this.restOptions.subqueryReadPreference;
additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference;
} else if (this.restOptions.readPreference) {
additionalOptions.readPreference = this.restOptions.readPreference;
}
var subquery = new RestQuery(
@@ -545,6 +554,8 @@ RestQuery.prototype.replaceDontSelect = function() {
if (this.restOptions.subqueryReadPreference) {
additionalOptions.readPreference = this.restOptions.subqueryReadPreference;
additionalOptions.subqueryReadPreference = this.restOptions.subqueryReadPreference;
} else if (this.restOptions.readPreference) {
additionalOptions.readPreference = this.restOptions.readPreference;
}
var subquery = new RestQuery(
@@ -809,6 +820,8 @@ function includePath(config, auth, response, path, restOptions = {}) {
includeRestOptions.readPreference = restOptions.includeReadPreference;
includeRestOptions.includeReadPreference =
restOptions.includeReadPreference;
} else if (restOptions.readPreference) {
includeRestOptions.readPreference = restOptions.readPreference;
}
const queryPromises = Object.keys(pointersHash).map(className => {

View File

@@ -3,7 +3,13 @@ import rest from '../rest';
import _ from 'lodash';
import Parse from 'parse/node';
const ALLOWED_GET_QUERY_KEYS = ['keys', 'include'];
const ALLOWED_GET_QUERY_KEYS = [
'keys',
'include',
'readPreference',
'includeReadPreference',
'subqueryReadPreference',
];
export class ClassesRouter extends PromiseRouter {
className(req) {
@@ -57,12 +63,21 @@ export class ClassesRouter extends PromiseRouter {
}
}
if (typeof body.keys == 'string') {
if (typeof body.keys === 'string') {
options.keys = body.keys;
}
if (body.include) {
options.include = String(body.include);
}
if (typeof body.readPreference === 'string') {
options.readPreference = body.readPreference;
}
if (typeof body.includeReadPreference === 'string') {
options.includeReadPreference = body.includeReadPreference;
}
if (typeof body.subqueryReadPreference === 'string') {
options.subqueryReadPreference = body.subqueryReadPreference;
}
return rest
.get(
@@ -154,6 +169,9 @@ export class ClassesRouter extends PromiseRouter {
'includeAll',
'redirectClassNameForKey',
'where',
'readPreference',
'includeReadPreference',
'subqueryReadPreference',
];
for (const key of Object.keys(body)) {
@@ -188,6 +206,15 @@ export class ClassesRouter extends PromiseRouter {
if (body.includeAll) {
options.includeAll = true;
}
if (typeof body.readPreference === 'string') {
options.readPreference = body.readPreference;
}
if (typeof body.includeReadPreference === 'string') {
options.includeReadPreference = body.includeReadPreference;
}
if (typeof body.subqueryReadPreference === 'string') {
options.subqueryReadPreference = body.subqueryReadPreference;
}
return options;
}

View File

@@ -101,7 +101,7 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
code === 10 /*\n*/ ||
code === 12 /*\f*/ ||
code === 160 /*\u00A0*/ ||
code === 65279 /*\uFEFF*/;
code === 65279; /*\uFEFF*/
if (start === -1) {
if (isWs) continue;
lastPos = start = i;
@@ -193,7 +193,7 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
// how the browser resolves relative URLs.
if (slashesDenoteHost || proto || /^\/\/[^@\/]+@[^@\/]+/.test(rest)) {
var slashes =
rest.charCodeAt(0) === 47 /*/*/ && rest.charCodeAt(1) === 47 /*/*/;
rest.charCodeAt(0) === 47 /*/*/ && rest.charCodeAt(1) === 47; /*/*/
if (slashes && !(proto && hostlessProtocol[proto])) {
rest = rest.slice(2);
this.slashes = true;
@@ -285,7 +285,7 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
// assume that it's an IPv6 address.
var ipv6Hostname =
hostname.charCodeAt(0) === 91 /*[*/ &&
hostname.charCodeAt(hostname.length - 1) === 93 /*]*/;
hostname.charCodeAt(hostname.length - 1) === 93; /*]*/
// validate a little.
if (!ipv6Hostname) {
@@ -868,11 +868,11 @@ Url.prototype.resolveObject = function(relative) {
// put the host back
if (psychotic) {
result.hostname = result.host = isAbsolute
? ''
: srcPath.length
? srcPath.shift()
: '';
if (isAbsolute) {
result.hostname = result.host = '';
} else {
result.hostname = result.host = srcPath.length ? srcPath.shift() : '';
}
//occasionally the auth can get stuck only in host
//this especially happens in cases like
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')