Skip to content

Commit 5518edc

Browse files
authored
Postgres adapter (#2012)
* Remove adaptiveCollection * Remove an adaptiveCollection use * Remove an adaptiveCollection * make adaptiveCollection private * Remove collection from mongoadapter * Move schema collection usage into mongo adapter * stop relying on mongo format for removing join tables * reduce usage of schemaCollection * remove uses of _collection * Move CLP setting into mongo adapter * remove all uses of schemaCollection * make schemaCollection private * remove transform from schemaCollection * rename some stuff * Tweak paramaters and stuff * reorder some params * reorder find() arguments * finishsh touching up argument order * Accept a database adapter as a parameter * First passing test with postgres! * Actually use the provided className * index on unique-indexes: c454180 Revert "Log objects rather than JSON stringified objects (#1922)" * Start dealing with test shittyness * Make specific server config for tests async * Fix email validation * Fix broken cloud code * Save callback to variable * undo * Fix tests * Setup travis * fix travis maybe * try removing db user * indentation? * remove postgres version setting * sudo maybe? * use postgres username * fix check for _PushStatus * excludes * remove db=mongo * allow postgres to fail * Fix allow failure * postgres 9.4 * Remove mongo implementations and fix test * Fix test leaving behind connections
1 parent d559cb2 commit 5518edc

20 files changed

+499
-318
lines changed

.travis.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@ language: node_js
22
node_js:
33
- '4.3'
44
- '6.1'
5+
services:
6+
- postgresql
7+
addons:
8+
postgresql: '9.4'
9+
before_script:
10+
- psql -c 'create database parse_server_postgres_adapter_test_database;' -U postgres
511
env:
612
global:
713
- COVERAGE_OPTION='./node_modules/babel-istanbul/lib/cli.js cover -x **/spec/**'
814
matrix:
15+
- PARSE_SERVER_TEST_DB=postgres
916
- MONGODB_VERSION=2.6.11
1017
- MONGODB_VERSION=3.0.8
1118
- MONGODB_VERSION=3.2.6
19+
matrix:
20+
fast_finish: true,
21+
allow_failures:
22+
- env: PARSE_SERVER_TEST_DB=postgres
1223
branches:
1324
only:
1425
- master

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"parse-server-push-adapter": "^1.0.0",
3939
"parse-server-s3-adapter": "^1.0.1",
4040
"parse-server-simple-mailgun-adapter": "^1.0.0",
41+
"pg-promise": "^4.4.0",
4142
"redis": "^2.5.0-1",
4243
"request": "^2.65.0",
4344
"request-promise": "^3.0.0",

spec/MongoStorageAdapter.spec.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDataba
99
describe('MongoStorageAdapter', () => {
1010
beforeEach(done => {
1111
new MongoStorageAdapter({ uri: databaseURI })
12-
.deleteAllSchemas()
12+
.deleteAllClasses()
1313
.then(done, fail);
1414
});
1515

@@ -49,7 +49,7 @@ describe('MongoStorageAdapter', () => {
4949

5050
it('stores objectId in _id', done => {
5151
let adapter = new MongoStorageAdapter({ uri: databaseURI });
52-
adapter.createObject('Foo', { objectId: 'abcde' }, { fields: { objectId: 'String' } })
52+
adapter.createObject('Foo', {}, { objectId: 'abcde' })
5353
.then(() => adapter._rawFind('Foo', {}))
5454
.then(results => {
5555
expect(results.length).toEqual(1);
@@ -70,10 +70,10 @@ describe('MongoStorageAdapter', () => {
7070
}
7171
};
7272
let adapter = new MongoStorageAdapter({ uri: databaseURI });
73-
adapter.createObject('APointerDarkly', obj, { fields: {
73+
adapter.createObject('APointerDarkly', { fields: {
7474
objectId: { type: 'String' },
7575
aPointer: { type: 'Pointer', targetClass: 'JustThePointer' },
76-
}})
76+
}}, obj)
7777
.then(() => adapter._rawFind('APointerDarkly', {}))
7878
.then(results => {
7979
expect(results.length).toEqual(1);
@@ -90,7 +90,7 @@ describe('MongoStorageAdapter', () => {
9090
let adapter = new MongoStorageAdapter({ uri: databaseURI });
9191
let schema = { fields : { subdoc: { type: 'Object' } } };
9292
let obj = { subdoc: {foo: 'bar', wu: 'tan'} };
93-
adapter.createObject('MyClass', obj, schema)
93+
adapter.createObject('MyClass', schema, obj)
9494
.then(() => adapter._rawFind('MyClass', {}))
9595
.then(results => {
9696
expect(results.length).toEqual(1);
@@ -99,7 +99,7 @@ describe('MongoStorageAdapter', () => {
9999
expect(mob.subdoc.foo).toBe('bar');
100100
expect(mob.subdoc.wu).toBe('tan');
101101
let obj = { 'subdoc.wu': 'clan' };
102-
return adapter.findOneAndUpdate('MyClass', {}, schema, obj);
102+
return adapter.findOneAndUpdate('MyClass', schema, {}, obj);
103103
})
104104
.then(() => adapter._rawFind('MyClass', {}))
105105
.then(results => {
@@ -127,15 +127,15 @@ describe('MongoStorageAdapter', () => {
127127
object: { type: 'Object' },
128128
date: { type: 'Date' },
129129
} };
130-
adapter.createObject('MyClass', obj, schema)
130+
adapter.createObject('MyClass', schema, obj)
131131
.then(() => adapter._rawFind('MyClass', {}))
132132
.then(results => {
133133
expect(results.length).toEqual(1);
134134
let mob = results[0];
135135
expect(mob.array instanceof Array).toBe(true);
136136
expect(typeof mob.object).toBe('object');
137137
expect(mob.date instanceof Date).toBe(true);
138-
return adapter.find('MyClass', {}, schema, {});
138+
return adapter.find('MyClass', schema, {}, {});
139139
})
140140
.then(results => {
141141
expect(results.length).toEqual(1);

spec/OAuth.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,9 +284,9 @@ describe('OAuth', function() {
284284
"Expiration should be cleared");
285285
// make sure the auth data is properly deleted
286286
var config = new Config(Parse.applicationId);
287-
config.database.adapter.find('_User', { objectId: model.id }, {
287+
config.database.adapter.find('_User', {
288288
fields: Object.assign({}, defaultColumns._Default, defaultColumns._Installation),
289-
}, {})
289+
}, { objectId: model.id }, {})
290290
.then(res => {
291291
expect(res.length).toBe(1);
292292
expect(res[0]._auth_data_myoauth).toBeUndefined();

spec/ParseAPI.spec.js

Lines changed: 51 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ describe('miscellaneous', function() {
2020
expect(typeof obj.id).toBe('string');
2121
expect(typeof obj.createdAt.toGMTString()).toBe('string');
2222
done();
23-
}, function(err) { console.log(err); });
23+
}, error => {
24+
fail(JSON.stringify(error));
25+
done();
26+
});
2427
});
2528

2629
it('get a TestObject', function(done) {
@@ -122,81 +125,63 @@ describe('miscellaneous', function() {
122125
});
123126

124127
it('ensure that if people already have duplicate users, they can still sign up new users', done => {
125-
reconfigureServer({})
128+
let config = new Config('test');
126129
// Remove existing data to clear out unique index
127-
.then(TestUtils.destroyAllDataPermanently)
130+
TestUtils.destroyAllDataPermanently()
131+
.then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'x', username: 'u' }))
132+
.then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'y', username: 'u' }))
133+
// Create a new server to try to recreate the unique indexes
134+
.then(reconfigureServer)
135+
.catch(() => {
136+
let user = new Parse.User();
137+
user.setPassword('asdf');
138+
user.setUsername('zxcv');
139+
// Sign up with new email still works
140+
return user.signUp().catch(fail);
141+
})
128142
.then(() => {
129-
let adapter = new MongoStorageAdapter({
130-
uri: 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase',
131-
collectionPrefix: 'test_',
132-
});
133-
adapter.createObject('_User', { objectId: 'x', username: 'u' }, requiredUserFields)
134-
.then(() => adapter.createObject('_User', { objectId: 'y', username: 'u' }, requiredUserFields))
135-
.then(() => {
136-
let user = new Parse.User();
137-
user.setPassword('asdf');
138-
user.setUsername('zxcv');
139-
return user.signUp();
140-
})
141-
.then(() => {
142-
let user = new Parse.User();
143-
user.setPassword('asdf');
144-
user.setUsername('u');
145-
user.signUp()
146-
.catch(error => {
147-
expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN);
148-
done();
149-
});
150-
})
151-
.catch(error => {
152-
fail(JSON.stringify(error));
153-
done();
154-
});
155-
}, () => {
156-
fail('destroyAllDataPermanently failed')
143+
let user = new Parse.User();
144+
user.setPassword('asdf');
145+
user.setUsername('u');
146+
// sign up with duplicate username doens't
147+
return user.signUp()
148+
})
149+
.catch(error => {
150+
expect(error.code).toEqual(Parse.Error.USERNAME_TAKEN);
157151
done();
158-
});
152+
})
159153
});
160154

161155
it('ensure that if people already have duplicate emails, they can still sign up new users', done => {
162-
reconfigureServer({})
163-
// Wipe out existing database with unique index so we can create a duplicate user
164-
.then(TestUtils.destroyAllDataPermanently)
156+
let config = new Config('test');
157+
// Remove existing data to clear out unique index
158+
TestUtils.destroyAllDataPermanently()
159+
.then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'x', email: '[email protected]' }))
160+
.then(() => config.database.adapter.createObject('_User', requiredUserFields, { objectId: 'y', email: '[email protected]' }))
161+
.then(reconfigureServer)
162+
.catch(() => {
163+
let user = new Parse.User();
164+
user.setPassword('asdf');
165+
user.setUsername('qqq');
166+
user.setEmail('[email protected]');
167+
return user.signUp().catch(fail);
168+
})
165169
.then(() => {
166-
let adapter = new MongoStorageAdapter({
167-
uri: 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase',
168-
collectionPrefix: 'test_',
169-
});
170-
adapter.createObject('_User', { objectId: 'x', email: '[email protected]' }, requiredUserFields)
171-
.then(() => adapter.createObject('_User', { objectId: 'y', email: '[email protected]' }, requiredUserFields))
172-
.then(() => {
173-
let user = new Parse.User();
174-
user.setPassword('asdf');
175-
user.setUsername('qqq');
176-
user.setEmail('[email protected]');
177-
return user.signUp();
178-
})
179-
.then(() => {
180-
let user = new Parse.User();
181-
user.setPassword('asdf');
182-
user.setUsername('www');
183-
user.setEmail('[email protected]');
184-
user.signUp()
185-
.catch(error => {
186-
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
187-
done();
188-
});
189-
})
190-
.catch(error => {
191-
fail(JSON.stringify(error));
192-
done();
193-
});
170+
let user = new Parse.User();
171+
user.setPassword('asdf');
172+
user.setUsername('www');
173+
user.setEmail('[email protected]');
174+
return user.signUp()
175+
})
176+
.catch(error => {
177+
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
178+
done();
194179
});
195180
});
196181

197182
it('ensure that if you try to sign up a user with a unique username and email, but duplicates in some other field that has a uniqueness constraint, you get a regular duplicate value error', done => {
198183
let config = new Config('test');
199-
config.database.adapter.ensureUniqueness('_User', ['randomField'], requiredUserFields)
184+
config.database.adapter.ensureUniqueness('_User', requiredUserFields, ['randomField'])
200185
.then(() => {
201186
let user = new Parse.User();
202187
user.setPassword('asdf');
@@ -228,8 +213,7 @@ describe('miscellaneous', function() {
228213
expect(typeof user.id).toEqual('string');
229214
expect(user.get('password')).toBeUndefined();
230215
expect(user.getSessionToken()).not.toBeUndefined();
231-
Parse.User.logOut();
232-
done();
216+
Parse.User.logOut().then(done);
233217
}, error: function(error) {
234218
fail(error);
235219
}
@@ -366,7 +350,7 @@ describe('miscellaneous', function() {
366350
return obj.save();
367351
}).then(() => {
368352
let config = new Config(appId);
369-
return config.database.adapter.find('TestObject', {}, { fields: {} }, {});
353+
return config.database.adapter.find('TestObject', { fields: {} }, {}, {});
370354
}).then((results) => {
371355
expect(results.length).toEqual(1);
372356
expect(results[0]['foo']).toEqual('bar');

spec/ParseGlobalConfig.spec.js

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ let Config = require('../src/Config');
77
describe('a GlobalConfig', () => {
88
beforeEach(done => {
99
let config = new Config('test');
10-
config.database.adapter.adaptiveCollection('_GlobalConfig')
11-
.then(coll => coll.upsertOne({ '_id': 1 }, { $set: { params: { companies: ['US', 'DK'] } } }))
12-
.then(() => { done(); });
10+
config.database.adapter.upsertOneObject(
11+
'_GlobalConfig',
12+
{ fields: {} },
13+
{ objectId: 1 },
14+
{ params: { companies: ['US', 'DK'] } }
15+
).then(done);
1316
});
1417

1518
it('can be retrieved', (done) => {
@@ -90,22 +93,23 @@ describe('a GlobalConfig', () => {
9093

9194
it('failed getting config when it is missing', (done) => {
9295
let config = new Config('test');
93-
config.database.adapter.adaptiveCollection('_GlobalConfig')
94-
.then(coll => coll.deleteOne({ '_id': 1 }))
95-
.then(() => {
96-
request.get({
97-
url : 'http://localhost:8378/1/config',
98-
json : true,
99-
headers: {
100-
'X-Parse-Application-Id': 'test',
101-
'X-Parse-Master-Key' : 'test'
102-
}
103-
}, (error, response, body) => {
104-
expect(response.statusCode).toEqual(200);
105-
expect(body.params).toEqual({});
106-
done();
107-
});
96+
config.database.adapter.deleteObjectsByQuery(
97+
'_GlobalConfig',
98+
{ fields: { params: { __type: 'String' } } },
99+
{ objectId: 1 }
100+
).then(() => {
101+
request.get({
102+
url : 'http://localhost:8378/1/config',
103+
json : true,
104+
headers: {
105+
'X-Parse-Application-Id': 'test',
106+
'X-Parse-Master-Key' : 'test'
107+
}
108+
}, (error, response, body) => {
109+
expect(response.statusCode).toEqual(200);
110+
expect(body.params).toEqual({});
111+
done();
108112
});
113+
});
109114
});
110-
111115
});

spec/ParseHooks.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ Parse.Hooks = require("../src/cloud-code/Parse.Hooks");
1010

1111
var port = 12345;
1212
var hookServerURL = "http://localhost:"+port;
13+
let AppCache = require('../src/cache').AppCache;
1314

1415
var app = express();
1516
app.use(bodyParser.json({ 'type': '*/*' }))
1617
app.listen(12345);
17-
let AppCache = require('../src/cache').AppCache;
1818

1919
describe('Hooks', () => {
2020

0 commit comments

Comments
 (0)