Skip to content

Add findOneAndDelete, findOneAndModify to MongoCollection, move most of usages to it. #759

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/Adapters/Storage/Mongo/MongoCollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,29 @@ export default class MongoCollection {
return this._mongoCollection.count(query, { skip, limit, sort });
}

// Atomically finds and updates an object based on query.
// The result is the promise with an object that was in the database !AFTER! changes.
// Postgres Note: Translates directly to `UPDATE * SET * ... RETURNING *`, which will return data after the change is done.
findOneAndUpdate(query, update) {
// arguments: query, sort, update, options(optional)
// Setting `new` option to true makes it return the after document, not the before one.
return this._mongoCollection.findAndModify(query, [], update, { new: true }).then(document => {
// Value is the object where mongo returns multiple fields.
return document.value;
})
}

// Atomically find and delete an object based on query.
// The result is the promise with an object that was in the database before deleting.
// Postgres Note: Translates directly to `DELETE * FROM ... RETURNING *`, which will return data after delete is done.
findOneAndDelete(query) {
// arguments: query, sort
return this._mongoCollection.findAndRemove(query, []).then(document => {
// Value is the object where mongo returns multiple fields.
return document.value;
});
}

drop() {
return this._mongoCollection.drop();
}
Expand Down
78 changes: 36 additions & 42 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,51 +142,45 @@ DatabaseController.prototype.update = function(className, query, update, options
var isMaster = !('acl' in options);
var aclGroup = options.acl || [];
var mongoUpdate, schema;
return this.loadSchema(acceptor).then((s) => {
schema = s;
if (!isMaster) {
return schema.validatePermission(className, aclGroup, 'update');
}
return Promise.resolve();
}).then(() => {

return this.handleRelationUpdates(className, query.objectId, update);
}).then(() => {
return this.collection(className);
}).then((coll) => {
var mongoWhere = transform.transformWhere(schema, className, query);
if (options.acl) {
var writePerms = [
{_wperm: {'$exists': false}}
];
for (var entry of options.acl) {
writePerms.push({_wperm: {'$in': [entry]}});
return this.loadSchema(acceptor)
.then(s => {
schema = s;
if (!isMaster) {
return schema.validatePermission(className, aclGroup, 'update');
}
return Promise.resolve();
})
.then(() => this.handleRelationUpdates(className, query.objectId, update))
.then(() => this.adaptiveCollection(className))
.then(collection => {
var mongoWhere = transform.transformWhere(schema, className, query);
if (options.acl) {
var writePerms = [
{_wperm: {'$exists': false}}
];
for (var entry of options.acl) {
writePerms.push({_wperm: {'$in': [entry]}});
}
mongoWhere = {'$and': [mongoWhere, {'$or': writePerms}]};
}
mongoUpdate = transform.transformUpdate(schema, className, update);
return collection.findOneAndUpdate(mongoWhere, mongoUpdate);
})
.then(result => {
if (!result) {
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Object not found.'));
}
mongoWhere = {'$and': [mongoWhere, {'$or': writePerms}]};
}

mongoUpdate = transform.transformUpdate(schema, className, update);

return coll.findAndModify(mongoWhere, {}, mongoUpdate, {});
}).then((result) => {
if (!result.value) {
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Object not found.'));
}
if (result.lastErrorObject.n != 1) {
return Promise.reject(new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Object not found.'));
}

var response = {};
var inc = mongoUpdate['$inc'];
if (inc) {
for (var key in inc) {
response[key] = (result.value[key] || 0) + inc[key];
let response = {};
let inc = mongoUpdate['$inc'];
if (inc) {
Object.keys(inc).forEach(key => {
response[key] = result[key];
});
}
}
return response;
});
return response;
});
};

// Processes relation-updating operations from a REST-format update.
Expand Down
88 changes: 37 additions & 51 deletions src/Controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,49 +40,43 @@ export class UserController extends AdaptableController {


verifyEmail(username, token) {

return new Promise((resolve, reject) => {

if (!this.shouldVerifyEmails) {
// Trying to verify email when not enabled
if (!this.shouldVerifyEmails) {
reject();
return;
}

var database = this.config.database;

database.collection('_User').then(coll => {
// TODO: Better error here.
return Promise.reject();
}

return this.config.database
.adaptiveCollection('_User')
.then(collection => {
// Need direct database access because verification token is not a parse field
return coll.findAndModify({
return collection.findOneAndUpdate({
username: username,
_email_verify_token: token,
}, null, {$set: {emailVerified: true}}, (err, doc) => {
if (err || !doc.value) {
reject(err);
} else {
resolve(doc.value);
}
});
_email_verify_token: token
}, {$set: {emailVerified: true}});
})
.then(document => {
if (!document) {
return Promise.reject();
}
return document;
});

});
}

checkResetTokenValidity(username, token) {
return new Promise((resolve, reject) => {
return this.config.database.collection('_User').then(coll => {
return coll.findOne({
username: username,
_perishable_token: token,
}, (err, doc) => {
if (err || !doc) {
reject(err);
} else {
resolve(doc);
}
});
return this.config.database.adaptiveCollection('_User')
.then(collection => {
return collection.find({
username: username,
_perishable_token: token
}, { limit: 1 });
})
.then(results => {
if (results.length != 1) {
return Promise.reject();
}
return results[0];
});
});
}

getUserIfNeeded(user) {
Expand Down Expand Up @@ -130,24 +124,16 @@ export class UserController extends AdaptableController {
}

setPasswordResetToken(email) {
var database = this.config.database;
var token = randomString(25);
return new Promise((resolve, reject) => {
return database.collection('_User').then(coll => {
let token = randomString(25);
return this.config.database
.adaptiveCollection('_User')
.then(collection => {
// Need direct database access because verification token is not a parse field
return coll.findAndModify({
email: email,
}, null, {$set: {_perishable_token: token}}, (err, doc) => {
if (err || !doc.value) {
console.error(err);
reject(err);
} else {
doc.value._perishable_token = token;
resolve(doc.value);
}
});
return collection.findOneAndUpdate(
{ email: email}, // query
{ $set: { _perishable_token: token } } // update
);
});
});
}

sendPasswordResetEmail(email) {
Expand Down
10 changes: 5 additions & 5 deletions src/Routers/SchemasRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,14 @@ function deleteSchema(req) {
.then(() => {
// We've dropped the collection now, so delete the item from _SCHEMA
// and clear the _Join collections
return req.config.database.collection('_SCHEMA')
.then(coll => coll.findAndRemove({_id: req.params.className}, []))
.then(doc => {
if (doc.value === null) {
return req.config.database.adaptiveCollection('_SCHEMA')
.then(coll => coll.findOneAndDelete({_id: req.params.className}))
.then(document => {
if (document === null) {
//tried to delete non-existent class
return Promise.resolve();
}
return removeJoinTables(req.config.database, doc.value);
return removeJoinTables(req.config.database, document);
});
})
.then(() => {
Expand Down