Skip to content

Commit

Permalink
index on unique-indexes: c454180 Revert "Log objects rather than JSON…
Browse files Browse the repository at this point in the history
… stringified objects (parse-community#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
  • Loading branch information
drew-gross committed Jun 13, 2016
1 parent 5518edc commit ce66705
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 28 deletions.
12 changes: 7 additions & 5 deletions spec/ParseAPI.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,22 @@ describe('miscellaneous', function() {
expect(obj2.id).toEqual(obj.id);
done();
},
error: fail
error: error => {
fail(JSON.stringify(error));
done();
}
});
});
});

it('create a valid parse user', function(done) {
fit('create a valid parse user', function(done) {
createTestUser(function(data) {
expect(data.id).not.toBeUndefined();
expect(data.getSessionToken()).not.toBeUndefined();
expect(data.get('password')).toBeUndefined();
done();
}, function(err) {
fail(err);
}, error => {
fail(JSON.stringify(error));
done();
});
});
Expand Down Expand Up @@ -85,7 +88,6 @@ describe('miscellaneous', function() {
});

it('ensure that email is uniquely indexed', done => {
let numCreated = 0;
let numFailed = 0;

let user1 = new Parse.User();
Expand Down
6 changes: 3 additions & 3 deletions spec/ParseInstallation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('Installations', () => {
'deviceType': device
};
rest.create(config, auth.nobody(config), '_Installation', input)
.then(() => config.database.adapter.find('_Installation', installationSchema, {}, {}))
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
.then(results => {
expect(results.length).toEqual(1);
var obj = results[0];
Expand All @@ -42,7 +42,7 @@ describe('Installations', () => {
'deviceType': device
};
rest.create(config, auth.nobody(config), '_Installation', input)
.then(() => config.database.adapter.find('_Installation', installationSchema, {}, {}))
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
.then(results => {
expect(results.length).toEqual(1);
var obj = results[0];
Expand All @@ -60,7 +60,7 @@ describe('Installations', () => {
'deviceType': device
};
rest.create(config, auth.nobody(config), '_Installation', input)
.then(() => config.database.adapter.find('_Installation', installationSchema, {}, {}))
.then(() => database.adapter.find('_Installation', installationSchema, {}, {}))
.then(results => {
expect(results.length).toEqual(1);
var obj = results[0];
Expand Down
2 changes: 1 addition & 1 deletion spec/ParseUser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2288,7 +2288,7 @@ describe('Parse.User testing', () => {
expect(err.code).toEqual(209);
expect(err.message).toEqual("Session token is expired.");
Parse.User.logOut() // Logout to prevent polluting CLI with messages
.then(done());
.then(done);
});
});
});
Expand Down
2 changes: 0 additions & 2 deletions spec/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ var ParseServer = require('../src/index').ParseServer;
var path = require('path');
var TestUtils = require('../src/index').TestUtils;
var MongoStorageAdapter = require('../src/Adapters/Storage/Mongo/MongoStorageAdapter');

const GridStoreAdapter = require('../src/Adapters/Files/GridStoreAdapter').GridStoreAdapter;
const PostgresStorageAdapter = require('../src/Adapters/Storage/Postgres/PostgresStorageAdapter');

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


var port = 8378;

let gridStoreAdapter = new GridStoreAdapter(mongoURI);
Expand Down
7 changes: 6 additions & 1 deletion src/Adapters/Storage/Mongo/MongoSchemaCollection.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import MongoCollection from './MongoCollection';

function mongoFieldToParseSchemaField(type) {
Expand Down Expand Up @@ -127,6 +126,12 @@ function mongoSchemaFromFieldsAndClassNameAndCLP(fields, className, classLevelPe
}
}

// Legacy mongo adapter knows about the difference between password and _hashed_password.
// Future database adapters will only know about _hashed_password.
// Note: Parse Server will bring back password with injectDefaultSchema, so we don't need
// to add it here.
delete mongoObject._hashed_password;

return mongoObject;
}

Expand Down
33 changes: 29 additions & 4 deletions src/Adapters/Storage/Postgres/PostgresStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ const pgp = require('pg-promise')();
const PostgresRelationDoesNotExistError = '42P01';
const PostgresDuplicateRelationError = '42P07';

const parseTypeToPostgresType = type => {
switch (type.type) {
case 'String': return 'text';
case 'Date': return 'timestamp';
case 'Object': return 'jsonb';
case 'Boolean': return 'boolean';
default: throw `no type for ${JSON.stringify(type)} yet`;
}
};

export class PostgresStorageAdapter {
// Private
Expand Down Expand Up @@ -38,13 +47,20 @@ export class PostgresStorageAdapter {
}

createClass(className, schema) {
return this._client.query('CREATE TABLE $<className:name> ()', { className })
let valuesArray = [];
let patternsArray = [];
Object.keys(schema.fields).forEach((fieldName, index) => {
valuesArray.push(fieldName);
valuesArray.push(parseTypeToPostgresType(schema.fields[fieldName]));
patternsArray.push(`$${index * 2 + 2}:name $${index * 2 + 3}:raw`);
});
return this._client.query(`CREATE TABLE $1:name (${patternsArray.join(',')})`, [className, ...valuesArray])
.then(() => this._client.query('INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)', { className, schema }))
}

addFieldIfNotExists(className, fieldName, type) {
// TODO: Doing this in a transaction is probably a good idea.
return this._client.query('ALTER TABLE "GameScore" ADD COLUMN "score" double precision', { className, fieldName })
return this._client.query('ALTER TABLE $<className:name> ADD COLUMN $<fieldName:name> text', { className, fieldName })
.catch(error => {
if (error.code === PostgresRelationDoesNotExistError) {
return this.createClass(className, { fields: { [fieldName]: type } })
Expand Down Expand Up @@ -137,7 +153,16 @@ export class PostgresStorageAdapter {

// TODO: remove the mongo format dependency
createObject(className, schema, object) {
return this._client.query('INSERT INTO "GameScore" (score) VALUES ($<score>)', { score: object.score })
console.log(object);
let columnsArray = [];
let valuesArray = [];
Object.keys(object).forEach(fieldName => {
columnsArray.push(fieldName);
valuesArray.push(object[fieldName]);
});
let columnsPattern = columnsArray.map((col, index) => `$${index + 2}~`).join(',');
let valuesPattern = valuesArray.map((val, index) => `$${index + 2 + columnsArray.length}`).join(',');
return this._client.query(`INSERT INTO $1~ (${columnsPattern}) VALUES (${valuesPattern})`, [className, ...columnsArray, ...valuesArray])
.then(() => ({ ops: [object] }));
}

Expand Down Expand Up @@ -165,7 +190,7 @@ export class PostgresStorageAdapter {

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

// Create a unique index. Unique indexes on nullable fields are not allowed. Since we don't
Expand Down
35 changes: 23 additions & 12 deletions src/Controllers/SchemaController.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ const fieldTypeIsInvalid = ({ type, targetClass }) => {
return undefined;
}

const convertSchemaToAdapterSchema = schema => {
schema = injectDefaultSchema(schema);
delete schema.fields.ACL;

if (schema.className === '_User') {
delete schema.fields.password;
schema.fields._hashed_password = { type: 'String' };
}

return schema;
}

const injectDefaultSchema = schema => ({
className: schema.className,
fields: {
Expand Down Expand Up @@ -293,7 +305,7 @@ class SchemaController {
return Promise.reject(validationError);
}

return this._dbAdapter.createClass(className, { fields, classLevelPermissions })
return this._dbAdapter.createClass(className, convertSchemaToAdapterSchema({ fields, classLevelPermissions, className }))
.catch(error => {
if (error && error.code === Parse.Error.DUPLICATE_VALUE) {
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, `Class ${className} already exists.`);
Expand Down Expand Up @@ -371,29 +383,28 @@ class SchemaController {

// Returns a promise that resolves successfully to the new schema
// object or fails with a reason.
// If 'freeze' is true, refuse to modify the schema.
enforceClassExists(className, freeze) {
enforceClassExists(className) {
if (this.data[className]) {
return Promise.resolve(this);
}
if (freeze) {
throw new Parse.Error(Parse.Error.INVALID_JSON,
'schema is frozen, cannot add: ' + className);
}
// We don't have this class. Update the schema
return this.addClassIfNotExists(className, {}).then(() => {
return this.addClassIfNotExists(className).then(() => {
// The schema update succeeded. Reload the schema
return this.reloadData();
}, () => {
}, error => {
// The schema update failed. This can be okay - it might
// have failed because there's a race condition and a different
// client is making the exact same schema update that we want.
// So just reload the schema.
return this.reloadData();
}).then(() => {
// Ensure that the schema now validates
return this.enforceClassExists(className, true);
}, () => {
if (this.data[className]) {
return this;
} else {
throw new Parse.Error(Parse.Error.INVALID_JSON, `Failed to add ${className}`);
}
}, error => {
// The schema still doesn't validate. Give up
throw new Parse.Error(Parse.Error.INVALID_JSON, 'schema class name does not revalidate');
});
Expand Down Expand Up @@ -622,7 +633,7 @@ class SchemaController {
}
return Promise.resolve(this);
}

// Validates the base CLP for an operation
testBaseCLP(className, aclGroup, operation) {
if (!this.perms[className] || !this.perms[className][operation]) {
Expand Down

0 comments on commit ce66705

Please sign in to comment.