Skip to content

Commit 452b737

Browse files
committed
WIP
1 parent 634d672 commit 452b737

File tree

7 files changed

+61
-95
lines changed

7 files changed

+61
-95
lines changed

spec/ParseAPI.spec.js

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('miscellaneous', function() {
4545
});
4646
});
4747

48-
fit('create a valid parse user', function(done) {
48+
it('create a valid parse user', function(done) {
4949
createTestUser(function(data) {
5050
expect(data.id).not.toBeUndefined();
5151
expect(data.getSessionToken()).not.toBeUndefined();
@@ -90,7 +90,7 @@ describe('miscellaneous', function() {
9090

9191
it('ensure that email is uniquely indexed', done => {
9292
let numFailed = 0;
93-
93+
let numCreated = 0;
9494
let user1 = new Parse.User();
9595
user1.setPassword('asdf');
9696
user1.setUsername('u1');
@@ -207,47 +207,6 @@ describe('miscellaneous', function() {
207207
});
208208
});
209209

210-
it('ensure that email is uniquely indexed', done => {
211-
let numCreated = 0;
212-
let numFailed = 0;
213-
214-
let user1 = new Parse.User();
215-
user1.setPassword('asdf');
216-
user1.setUsername('u1');
217-
user1.setEmail('[email protected]');
218-
let p1 = user1.signUp();
219-
p1.then(user => {
220-
numCreated++;
221-
expect(numCreated).toEqual(1);
222-
})
223-
.catch(error => {
224-
numFailed++;
225-
expect(numFailed).toEqual(1);
226-
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
227-
});
228-
229-
let user2 = new Parse.User();
230-
user2.setPassword('asdf');
231-
user2.setUsername('u2');
232-
user2.setEmail('[email protected]');
233-
let p2 = user2.signUp();
234-
p2.then(user => {
235-
numCreated++;
236-
expect(numCreated).toEqual(1);
237-
})
238-
.catch(error => {
239-
numFailed++;
240-
expect(numFailed).toEqual(1);
241-
expect(error.code).toEqual(Parse.Error.EMAIL_TAKEN);
242-
});
243-
Parse.Promise.all([p1, p2])
244-
.then(() => {
245-
fail('one of the users should not have been created');
246-
done();
247-
})
248-
.catch(done);
249-
});
250-
251210
it('ensure that if people already have duplicate emails, they can still sign up new users', done => {
252211
let config = new Config('test');
253212
// Remove existing data to clear out unique index
@@ -318,7 +277,7 @@ describe('miscellaneous', function() {
318277
}, fail);
319278
});
320279

321-
fit('increment with a user object', function(done) {
280+
it('increment with a user object', function(done) {
322281
createTestUser().then((user) => {
323282
user.increment('foo');
324283
return user.save();

spec/ParseQuery.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,7 @@ describe('Parse.Query testing', () => {
11861186
});
11871187
});
11881188

1189-
it("regexes with invalid options fail", function(done) {
1189+
fit("regexes with invalid options fail", function(done) {
11901190
var query = new Parse.Query(TestObject);
11911191
query.matches("myString", "FootBall", "some invalid option");
11921192
query.find(expectError(Parse.Error.INVALID_QUERY, done));

src/Adapters/Storage/Mongo/MongoCollection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default class MongoCollection {
5252
// If there is nothing that matches the query - does insert
5353
// Postgres Note: `INSERT ... ON CONFLICT UPDATE` that is available since 9.5.
5454
upsertOne(query, update) {
55-
return this._mongoCollection.update(query, update, { upsert: true });
55+
return this._mongoCollection.update(query, update, { upsert: true })
5656
}
5757

5858
updateOne(query, update) {

src/Adapters/Storage/Mongo/MongoTransform.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -281,10 +281,14 @@ const parseObjectToMongoObjectForCreate = (className, restCreate, schema) => {
281281
}
282282

283283
// Use the legacy mongo format for createdAt and updatedAt
284-
mongoCreate._created_at = mongoCreate.createdAt.iso;
285-
delete mongoCreate.createdAt;
286-
mongoCreate._updated_at = mongoCreate.updatedAt.iso;
287-
delete mongoCreate.updatedAt;
284+
if (mongoCreate.createdAt) {
285+
mongoCreate._created_at = new Date(mongoCreate.createdAt.iso || mongoCreate.createdAt);
286+
delete mongoCreate.createdAt;
287+
}
288+
if (mongoCreate.updatedAt) {
289+
mongoCreate._updated_at = new Date(mongoCreate.updatedAt.iso || mongoCreate.updatedAt);
290+
delete mongoCreate.updatedAt;
291+
}
288292

289293
return mongoCreate;
290294
}

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ export class PostgresStorageAdapter {
166166
createObject(className, schema, object) {
167167
let columnsArray = [];
168168
let valuesArray = [];
169+
console.log('creating');
170+
console.log(schema);
171+
console.log(object);
172+
console.log(className);
173+
console.log(new Error().stack);
169174
Object.keys(object).forEach(fieldName => {
170175
columnsArray.push(fieldName);
171176
switch (schema.fields[fieldName].type) {

src/Controllers/DatabaseController.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,10 @@ DatabaseController.prototype.find = function(className, query, {
648648
let classExists = true;
649649
return this.loadSchema()
650650
.then(schemaController => {
651-
return schemaController.getOneSchema(className)
651+
//Allow volatile classes if querying with Master (for _PushStatus)
652+
//TODO: Move volatile classes concept into mongo adatper, postgres adapter shouldn't care
653+
//that api.parse.com breaks when _PushStatus exists in mongo.
654+
return schemaController.getOneSchema(className, isMaster)
652655
.catch(error => {
653656
// Behaviour for non-existent classes is kinda weird on Parse.com. Probably doesn't matter too much.
654657
// For now, pretend the class exists but has no objects,

src/Controllers/SchemaController.js

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ const requiredColumns = Object.freeze({
9797

9898
const systemClasses = Object.freeze(['_User', '_Installation', '_Role', '_Session', '_Product', '_PushStatus']);
9999

100-
const volatileClasses = Object.freeze(['_PushStatus']);
100+
const volatileClasses = Object.freeze(['_PushStatus', '_Hooks', '_GlobalConfig']);
101101

102102
// 10 alpha numberic chars + uppercase
103103
const userIdRegex = /^[a-zA-Z0-9]{10}$/;
@@ -244,6 +244,14 @@ const injectDefaultSchema = schema => ({
244244
classLevelPermissions: schema.classLevelPermissions,
245245
})
246246

247+
const dbTypeMatchesObjectType = (dbType, objectType) => {
248+
if (dbType.type !== objectType.type) return false;
249+
if (dbType.targetClass !== objectType.targetClass) return false;
250+
if (dbType === objectType.type) return true;
251+
if (dbType.type === objectType.type) return true;
252+
return false;
253+
}
254+
247255
// Stores the entire schema of the app in a weird hybrid format somewhere between
248256
// the mongo format and the Parse format. Soon, this will all be Parse format.
249257
class SchemaController {
@@ -358,7 +366,7 @@ class SchemaController {
358366
.then(() => {
359367
let promises = insertedFields.map(fieldName => {
360368
const type = submittedFields[fieldName];
361-
return this.validateField(className, fieldName, type);
369+
return this.enforceFieldExists(className, fieldName, type);
362370
});
363371
return Promise.all(promises);
364372
})
@@ -460,49 +468,36 @@ class SchemaController {
460468
// object if the provided className-fieldName-type tuple is valid.
461469
// The className must already be validated.
462470
// If 'freeze' is true, refuse to update the schema for this field.
463-
validateField(className, fieldName, type, freeze) {
464-
return this.reloadData().then(() => {
465-
if (fieldName.indexOf(".") > 0) {
466-
// subdocument key (x.y) => ok if x is of type 'object'
467-
fieldName = fieldName.split(".")[ 0 ];
468-
type = 'Object';
469-
}
471+
enforceFieldExists(className, fieldName, type, freeze) {
472+
if (fieldName.indexOf(".") > 0) {
473+
// subdocument key (x.y) => ok if x is of type 'object'
474+
fieldName = fieldName.split(".")[ 0 ];
475+
type = 'Object';
476+
}
477+
if (!fieldNameIsValid(fieldName)) {
478+
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
479+
}
470480

471-
if (!fieldNameIsValid(fieldName)) {
472-
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
481+
// If someone tries to create a new field with null/undefined as the value, return;
482+
if (!type) {
483+
return Promise.resolve(this);
484+
}
485+
486+
return this.reloadData().then(() => {
487+
let expectedType = this.getExpectedType(className, fieldName);
488+
if (typeof type === 'string') {
489+
type = { type };
473490
}
474491

475-
let expected = this.data[className][fieldName];
476-
if (expected) {
477-
expected = (expected === 'map' ? 'Object' : expected);
478-
if (expected.type && type.type
479-
&& expected.type == type.type
480-
&& expected.targetClass == type.targetClass) {
481-
return Promise.resolve(this);
482-
} else if (expected == type || expected.type == type) {
483-
return Promise.resolve(this);
484-
} else {
492+
if (expectedType) {
493+
if (!dbTypeMatchesObjectType(expectedType, type)) {
485494
throw new Parse.Error(
486495
Parse.Error.INCORRECT_TYPE,
487-
`schema mismatch for ${className}.${fieldName}; expected ${expected.type || expected} but got ${type}`
496+
`schema mismatch for ${className}.${fieldName}; expected ${expectedType.type || expectedType} but got ${type}`
488497
);
489498
}
490499
}
491500

492-
if (freeze) {
493-
throw new Parse.Error(Parse.Error.INVALID_JSON, `schema is frozen, cannot add ${fieldName} field`);
494-
}
495-
496-
// We don't have this field, but if the value is null or undefined,
497-
// we won't update the schema until we get a value with a type.
498-
if (!type) {
499-
return Promise.resolve(this);
500-
}
501-
502-
if (typeof type === 'string') {
503-
type = { type };
504-
}
505-
506501
return this._dbAdapter.addFieldIfNotExists(className, fieldName, type).then(() => {
507502
// The update succeeded. Reload the schema
508503
return this.reloadData();
@@ -513,11 +508,10 @@ class SchemaController {
513508
return this.reloadData();
514509
}).then(() => {
515510
// Ensure that the schema now validates
516-
return this.validateField(className, fieldName, type, true);
517-
}, (error) => {
518-
// The schema still doesn't validate. Give up
519-
throw new Parse.Error(Parse.Error.INVALID_JSON,
520-
'schema key will not revalidate');
511+
if (!dbTypeMatchesObjectType(this.getExpectedType(className, fieldName), type)) {
512+
throw new Parse.Error(Parse.Error.INVALID_JSON, `Could not add field ${fieldName}`);
513+
}
514+
return this;
521515
});
522516
});
523517
}
@@ -674,9 +668,10 @@ class SchemaController {
674668

675669
// Returns the expected type for a className+key combination
676670
// or undefined if the schema is not set
677-
getExpectedType(className, key) {
671+
getExpectedType(className, fieldName) {
678672
if (this.data && this.data[className]) {
679-
return this.data[className][key];
673+
const expectedType = this.data[className][fieldName]
674+
return expectedType === 'map' ? 'Object' : expectedType;
680675
}
681676
return undefined;
682677
};
@@ -727,7 +722,7 @@ function buildMergedSchemaObject(existingFields, putRequest) {
727722
// validates this field once the schema loads.
728723
function thenValidateField(schemaPromise, className, key, type) {
729724
return schemaPromise.then((schema) => {
730-
return schema.validateField(className, key, type);
725+
return schema.enforceFieldExists(className, key, type);
731726
});
732727
}
733728

0 commit comments

Comments
 (0)