Skip to content

Commit f75c8b3

Browse files
committed
index on unique-indexes: c454180 Revert "Log objects rather than JSON stringified objects (#1922)"
reconfigure username/email tests Fix broken cloud code Save callback to variable undo Fix all tests where connections are left open after server closes. Fix issues caused by missing gridstore adapter remove uses of _collection reorder find() arguments Accept a database adapter as a parameter sudo maybe? use postgres username reorder find() arguments Build objects with default fields correctly Don't tell adapter about ACL WIP
1 parent 693707b commit f75c8b3

File tree

7 files changed

+68
-27
lines changed

7 files changed

+68
-27
lines changed

spec/ParseAPI.spec.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,22 @@ describe('miscellaneous', function() {
3737
expect(obj2.id).toEqual(obj.id);
3838
done();
3939
},
40-
error: fail
40+
error: error => {
41+
fail(JSON.stringify(error));
42+
done();
43+
}
4144
});
4245
});
4346
});
4447

45-
it('create a valid parse user', function(done) {
48+
fit('create a valid parse user', function(done) {
4649
createTestUser(function(data) {
4750
expect(data.id).not.toBeUndefined();
4851
expect(data.getSessionToken()).not.toBeUndefined();
4952
expect(data.get('password')).toBeUndefined();
5053
done();
51-
}, function(err) {
52-
fail(err);
54+
}, error => {
55+
fail(JSON.stringify(error));
5356
done();
5457
});
5558
});
@@ -86,7 +89,6 @@ describe('miscellaneous', function() {
8689
});
8790

8891
it('ensure that email is uniquely indexed', done => {
89-
let numCreated = 0;
9092
let numFailed = 0;
9193

9294
let user1 = new Parse.User();

spec/ParseInstallation.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('Installations', () => {
2424
'deviceType': device
2525
};
2626
rest.create(config, auth.nobody(config), '_Installation', input)
27-
.then(() => config.database.adapter.find('_Installation', installationSchema, {}, {}))
27+
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
2828
.then(results => {
2929
expect(results.length).toEqual(1);
3030
var obj = results[0];
@@ -42,7 +42,7 @@ describe('Installations', () => {
4242
'deviceType': device
4343
};
4444
rest.create(config, auth.nobody(config), '_Installation', input)
45-
.then(() => config.database.adapter.find('_Installation', installationSchema, {}, {}))
45+
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
4646
.then(results => {
4747
expect(results.length).toEqual(1);
4848
var obj = results[0];
@@ -60,7 +60,7 @@ describe('Installations', () => {
6060
'deviceType': device
6161
};
6262
rest.create(config, auth.nobody(config), '_Installation', input)
63-
.then(() => config.database.adapter.find('_Installation', installationSchema, {}, {}))
63+
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
6464
.then(results => {
6565
expect(results.length).toEqual(1);
6666
var obj = results[0];

spec/ParseUser.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2288,7 +2288,7 @@ describe('Parse.User testing', () => {
22882288
expect(err.code).toEqual(209);
22892289
expect(err.message).toEqual("Session token is expired.");
22902290
Parse.User.logOut() // Logout to prevent polluting CLI with messages
2291-
.then(done());
2291+
.then(done);
22922292
});
22932293
});
22942294
});

spec/helper.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ var ParseServer = require('../src/index').ParseServer;
1111
var path = require('path');
1212
var TestUtils = require('../src/index').TestUtils;
1313
var MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter');
14-
1514
const GridStoreAdapter = require('../src/Adapters/Files/GridStoreAdapter').GridStoreAdapter;
1615
const PostgresStorageAdapter = require('../src/Adapters/Storage/Postgres/PostgresStorageAdapter');
1716

@@ -30,7 +29,6 @@ if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
3029
})
3130
}
3231

33-
3432
var port = 8378;
3533

3634
let gridStoreAdapter = new GridStoreAdapter(mongoURI);

src/Adapters/Storage/Mongo/MongoSchemaCollection.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import MongoCollection from './MongoCollection';
32

43
function mongoFieldToParseSchemaField(type) {
@@ -127,6 +126,12 @@ function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPe
127126
}
128127
}
129128

129+
// Legacy mongo adapter knows about the difference between password and _hashed_password.
130+
// Future database adapters will only know about _hashed_password.
131+
// Note: Parse Server will bring back password with injectDefaultSchema, so we don't need
132+
// to add it here.
133+
delete mongoObject._hashed_password;
134+
130135
return mongoObject;
131136
}
132137

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ const pgp = require('pg-promise')();
33
const PostgresRelationDoesNotExistError = '42P01';
44
const PostgresDuplicateRelationError = '42P07';
55

6+
const parseTypeToPostgresType = type => {
7+
switch (type.type) {
8+
case 'String': return 'text';
9+
case 'Date': return 'timestamp';
10+
case 'Object': return 'jsonb';
11+
case 'Boolean': return 'boolean';
12+
default: throw `no type for ${JSON.stringify(type)} yet`;
13+
}
14+
};
615

716
export class PostgresStorageAdapter {
817
// Private
@@ -37,13 +46,20 @@ export class PostgresStorageAdapter {
3746
}
3847

3948
createClass(className, schema) {
40-
return this._client.query('CREATE TABLE $<className:name> ()', { className })
49+
let valuesArray = [];
50+
let patternsArray = [];
51+
Object.keys(schema.fields).forEach((fieldName, index) => {
52+
valuesArray.push(fieldName);
53+
valuesArray.push(parseTypeToPostgresType(schema.fields[fieldName]));
54+
patternsArray.push(`$${index * 2 + 2}:name $${index * 2 + 3}:raw`);
55+
});
56+
return this._client.query(`CREATE TABLE $1:name (${patternsArray.join(',')})`, [className, ...valuesArray])
4157
.then(() => this._client.query('INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)', { className, schema }))
4258
}
4359

4460
addFieldIfNotExists(className, fieldName, type) {
4561
// TODO: Doing this in a transaction is probably a good idea.
46-
return this._client.query('ALTER TABLE "GameScore" ADD COLUMN "score" double precision', { className, fieldName })
62+
return this._client.query('ALTER TABLE $<className:name> ADD COLUMN $<fieldName:name> text', { className, fieldName })
4763
.catch(error => {
4864
if (error.code === PostgresRelationDoesNotExistError) {
4965
return this.createClass(className, { fields: { [fieldName]: type } })
@@ -136,7 +152,16 @@ export class PostgresStorageAdapter {
136152

137153
// TODO: remove the mongo format dependency
138154
createObject(className, schema, object) {
139-
return this._client.query('INSERT INTO "GameScore" (score) VALUES ($<score>)', { score: object.score })
155+
console.log(object);
156+
let columnsArray = [];
157+
let valuesArray = [];
158+
Object.keys(object).forEach(fieldName => {
159+
columnsArray.push(fieldName);
160+
valuesArray.push(object[fieldName]);
161+
});
162+
let columnsPattern = columnsArray.map((col, index) => `$${index + 2}~`).join(',');
163+
let valuesPattern = valuesArray.map((val, index) => `$${index + 2 + columnsArray.length}`).join(',');
164+
return this._client.query(`INSERT INTO $1~ (${columnsPattern}) VALUES (${valuesPattern})`, [className, ...columnsArray, ...valuesArray])
140165
.then(() => ({ ops: [object] }));
141166
}
142167

@@ -164,7 +189,7 @@ export class PostgresStorageAdapter {
164189

165190
// Executes a find. Accepts: className, query in Parse format, and { skip, limit, sort }.
166191
find(className, schema, query, { skip, limit, sort }) {
167-
return this._client.query("SELECT * FROM $<className>", { className })
192+
return this._client.query("SELECT * FROM $<className:name>", { className })
168193
}
169194

170195
// Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't

src/Controllers/SchemaController.js

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,18 @@ const fieldTypeIsInvalid = ({ type, targetClass }) => {
220220
return undefined;
221221
}
222222

223+
const convertSchemaToAdapterSchema = schema => {
224+
schema = injectDefaultSchema(schema);
225+
delete schema.fields.ACL;
226+
227+
if (schema.className === '_User') {
228+
delete schema.fields.password;
229+
schema.fields._hashed_password = { type: 'String' };
230+
}
231+
232+
return schema;
233+
}
234+
223235
const injectDefaultSchema = schema => ({
224236
className: schema.className,
225237
fields: {
@@ -293,7 +305,7 @@ class SchemaController {
293305
return Promise.reject(validationError);
294306
}
295307

296-
return this._dbAdapter.createClass(className, { fields, classLevelPermissions })
308+
return this._dbAdapter.createClass(className, convertSchemaToAdapterSchema({ fields, classLevelPermissions, className }))
297309
.catch(error => {
298310
if (error && error.code === Parse.Error.DUPLICATE_VALUE) {
299311
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`);
@@ -360,29 +372,28 @@ class SchemaController {
360372

361373
// Returns a promise that resolves successfully to the new schema
362374
// object or fails with a reason.
363-
// If 'freeze' is true, refuse to modify the schema.
364-
enforceClassExists(className, freeze) {
375+
enforceClassExists(className) {
365376
if (this.data[className]) {
366377
return Promise.resolve(this);
367378
}
368-
if (freeze) {
369-
throw new Parse.Error(Parse.Error.INVALID_JSON,
370-
'schema is frozen, cannot add: ' + className);
371-
}
372379
// We don't have this class. Update the schema
373-
return this.addClassIfNotExists(className, {}).then(() => {
380+
return this.addClassIfNotExists(className).then(() => {
374381
// The schema update succeeded. Reload the schema
375382
return this.reloadData();
376-
}, () => {
383+
}, error => {
377384
// The schema update failed. This can be okay - it might
378385
// have failed because there's a race condition and a different
379386
// client is making the exact same schema update that we want.
380387
// So just reload the schema.
381388
return this.reloadData();
382389
}).then(() => {
383390
// Ensure that the schema now validates
384-
return this.enforceClassExists(className, true);
385-
}, () => {
391+
if (this.data[className]) {
392+
return this;
393+
} else {
394+
throw new Parse.Error(Parse.Error.INVALID_JSON, `Failed to add ${className}`);
395+
}
396+
}, error => {
386397
// The schema still doesn't validate. Give up
387398
throw new Parse.Error(Parse.Error.INVALID_JSON, 'schema class name does not revalidate');
388399
});

0 commit comments

Comments
 (0)