Skip to content

Commit

Permalink
Merge pull request #12802 from meteor/making-mongo-errors-clearer
Browse files Browse the repository at this point in the history
Making mongo errors clearer
  • Loading branch information
Grubba27 committed Oct 6, 2023
2 parents 403e74a + 35eea18 commit eeaa406
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 70 deletions.
6 changes: 3 additions & 3 deletions packages/accounts-2fa/2fa-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Meteor.methods({
});
const svg = new QRCode(uri).svg();

await Meteor.users.update(
await Meteor.users.updateAsync(
{ _id: user._id },
{
$set: {
Expand Down Expand Up @@ -94,7 +94,7 @@ Meteor.methods({
Accounts._handleError('Invalid 2FA code', true, 'invalid-2fa-code');
}

await Meteor.users.update(
await Meteor.users.updateAsync(
{ _id: user._id },
{
$set: {
Expand All @@ -113,7 +113,7 @@ Meteor.methods({
throw new Meteor.Error(400, 'No user logged in.');
}

await Meteor.users.update(
await Meteor.users.updateAsync(
{ _id: userId },
{
$unset: {
Expand Down
72 changes: 40 additions & 32 deletions packages/accounts-password/email_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ testAsyncMulti("accounts emails - reset password flow", [
}));
},
function (test, expect) {
Meteor.logout(expect((error) => {
Meteor.logout(expect(async (error) => {
test.equal(error, undefined);
test.equal(Meteor.user(), null);
test.equal(await Meteor.user(), null);
}));
},
function (test, expect) {
Expand All @@ -62,9 +62,9 @@ testAsyncMulti("accounts emails - reset password flow", [
}));
},
function (test, expect) {
Meteor.logout(expect((error) => {
Meteor.logout(expect(async (error) => {
test.equal(error, undefined);
test.equal(Meteor.user(), null);
test.equal(await Meteor.user(), null);
}));
}
]);
Expand Down Expand Up @@ -168,40 +168,45 @@ testAsyncMulti("accounts emails - verify email flow", [
{email: this.email, password: 'foobar'},
loggedIn(test, expect));
},
function (test, expect) {
test.equal(Meteor.user().emails.length, 1);
test.equal(Meteor.user().emails[0].address, this.email);
test.isFalse(Meteor.user().emails[0].verified);
async function (test, expect) {
const u = await Meteor.user();
test.equal(u.emails.length, 1);
test.equal(u.emails[0].address, this.email);
test.isFalse(u.emails[0].verified);
// We should NOT be publishing things like verification tokens!
test.isFalse(Object.prototype.hasOwnProperty.call(Meteor.user(), 'services'));
test.isFalse(Object.prototype.hasOwnProperty.call(u, 'services'));
},
function (test, expect) {
getVerifyEmailToken(this.email, test, expect);
},
function (test, expect) {
// Log out, to test that verifyEmail logs us back in.
Meteor.logout(expect((error) => {
Meteor.logout(expect(async (error) => {
test.equal(error, undefined);
test.equal(Meteor.user(), null);
test.equal(await Meteor.user(), null);
}));
},
function (test, expect) {
Accounts.verifyEmail(verifyEmailToken,
loggedIn(test, expect));
},
function (test, expect) {
test.equal(Meteor.user().emails.length, 1);
test.equal(Meteor.user().emails[0].address, this.email);
test.isTrue(Meteor.user().emails[0].verified);
async function (test, expect) {
const u = await Meteor.user();

test.equal(u.emails.length, 1);
test.equal(u.emails[0].address, this.email);
test.isTrue(u.emails[0].verified);
},
function (test, expect) {
Accounts.connection.call(
"addEmailForTestAndVerify", this.anotherEmail,
expect((error, result) => {
expect(async (error, result) => {
const u = await Meteor.user();

test.isFalse(error);
test.equal(Meteor.user().emails.length, 2);
test.equal(Meteor.user().emails[1].address, this.anotherEmail);
test.isFalse(Meteor.user().emails[1].verified);
test.equal(u.emails.length, 2);
test.equal(u.emails[1].address, this.anotherEmail);
test.isFalse(u.emails[1].verified);
}));
},
function (test, expect) {
Expand All @@ -210,9 +215,9 @@ testAsyncMulti("accounts emails - verify email flow", [
function (test, expect) {
// Log out, to test that verifyEmail logs us back in. (And if we don't
// do that, waitUntilLoggedIn won't be able to prevent race conditions.)
Meteor.logout(expect((error) => {
Meteor.logout(expect(async (error) => {
test.equal(error, undefined);
test.equal(Meteor.user(), null);
test.equal(await Meteor.user(), null);
}));
},
function (test, expect) {
Expand All @@ -226,11 +231,12 @@ testAsyncMulti("accounts emails - verify email flow", [
function (test, expect) {
Accounts.connection.call(
"addEmailForTestAndVerify", this.anotherEmailCaps,
expect((error, result) => {
expect(async (error, result) => {
const u = await Meteor.user();
test.isFalse(error);
test.equal(Meteor.user().emails.length, 3);
test.equal(Meteor.user().emails[2].address, this.anotherEmailCaps);
test.isFalse(Meteor.user().emails[2].verified);
test.equal(u.emails.length, 3);
test.equal(u.emails[2].address, this.anotherEmailCaps);
test.isFalse(u.emails[2].verified);
}));
},
function (test, expect) {
Expand All @@ -239,23 +245,25 @@ testAsyncMulti("accounts emails - verify email flow", [
function (test, expect) {
// Log out, to test that verifyEmail logs us back in. (And if we don't
// do that, waitUntilLoggedIn won't be able to prevent race conditions.)
Meteor.logout(expect((error) => {
Meteor.logout(expect(async (error) => {
test.equal(error, undefined);
test.equal(Meteor.user(), null);
test.equal(await Meteor.user(), null);
}));
},
function (test, expect) {
Accounts.verifyEmail(verifyEmailToken,
loggedIn(test, expect));
},
function (test, expect) {
test.equal(Meteor.user().emails[2].address, this.anotherEmailCaps);
test.isTrue(Meteor.user().emails[2].verified);
async function (test, expect) {
const u = await Meteor.user();

test.equal(u.emails[2].address, this.anotherEmailCaps);
test.isTrue(u.emails[2].verified);
},
function (test, expect) {
Meteor.logout(expect((error) => {
Meteor.logout(expect(async (error) => {
test.equal(error, undefined);
test.equal(Meteor.user(), null);
test.equal(await Meteor.user(), null);
}));
}
]);
Expand Down
2 changes: 1 addition & 1 deletion packages/accounts-password/email_tests_setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Meteor.methods(
addEmailForTestAndVerify:
async email => {
check(email, String);
await Meteor.users.update(
await Meteor.users.updateAsync(
{ _id: Accounts.userId() },
{ $push: { emails: { address: email, verified: false } } });
await Accounts.sendVerificationEmail(Accounts.userId(), email);
Expand Down
4 changes: 2 additions & 2 deletions packages/accounts-password/password_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,8 @@ if (Meteor.isClient) (() => {
// test Meteor.user(). This test properly belongs in
// accounts-base/accounts_tests.js, but this is where the tests that
// actually log in are.
function (test, expect) {
const clientUser = Meteor.user();
async function (test, expect) {
const clientUser = await Meteor.user();
Accounts.connection.call('testMeteorUser', expect((err, result) => {
test.equal(result._id, clientUser._id);
test.equal(result.username, clientUser.username);
Expand Down
4 changes: 2 additions & 2 deletions packages/accounts-password/password_tests_setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Meteor.methods(
if (!this.userId) throw new Error("Not logged in!");
await Meteor
.users
.update(this.userId, { $unset: { profile: 1, username: 1 } });
.updateAsync(this.userId, { $unset: { profile: 1, username: 1 } });
},

expireTokens:
Expand All @@ -137,6 +137,6 @@ Meteor.methods(
},

removeUser:
async username => await Meteor.users.remove({ "username": username }),
async username => await Meteor.users.removeAsync({ "username": username }),
}
);
2 changes: 2 additions & 0 deletions packages/minimongo/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ export const ASYNC_COLLECTION_METHODS = [
];

export const ASYNC_CURSOR_METHODS = ['count', 'fetch', 'forEach', 'map'];

export const CLIENT_ONLY_METHODS = ["findOne", "insert", "remove", "update", "upsert"];
17 changes: 1 addition & 16 deletions packages/mongo/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,7 @@ Object.assign(Mongo.Collection.prototype, {
// If the user provided a callback and the collection implements this
// operation asynchronously, then queryRet will be undefined, and the
// result will be returned through the callback instead.
return this._collection.updateAsync(
return this._collection.update(
selector,
modifier,
options,
Expand Down Expand Up @@ -1258,18 +1258,3 @@ function popCallbackFromArgs(args) {
}
}


// XXX: IN Meteor 3.x this code was not working....
// It throws an error when trying to call a method on the collection.
// the error normally is:
// TypeError: this[methodName] is not a function
// ASYNC_COLLECTION_METHODS.forEach(methodName => {
// const methodNameAsync = getAsyncMethodName(methodName);
// Mongo.Collection.prototype[methodNameAsync] = function(...args) {
// try {
// return Promise.resolve(this[methodName](...args));
// } catch (error) {
// return Promise.reject(error);
// }
// };
// });
19 changes: 19 additions & 0 deletions packages/mongo/mongo_driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var MongoDB = NpmModuleMongodb;
import { DocFetcher } from "./doc_fetcher.js";
import {
ASYNC_CURSOR_METHODS,
CLIENT_ONLY_METHODS,
getAsyncMethodName
} from "meteor/minimongo/constants";
import { Meteor } from "meteor/meteor";
Expand Down Expand Up @@ -359,6 +360,7 @@ MongoConnection.prototype.insertAsync = async function (collection_name, documen
});
};


// Cause queries that may be affected by the selector to poll in this write
// fence.
MongoConnection.prototype._refresh = async function (collectionName, selector) {
Expand Down Expand Up @@ -791,6 +793,17 @@ MongoConnection.prototype.dropIndexAsync = async function (collectionName, index
var indexName = await collection.dropIndex(index);
};


CLIENT_ONLY_METHODS.forEach(function (m) {
MongoConnection.prototype[m] = function () {
throw new Error(
`${m} + is not available on the server. Please use ${getAsyncMethodName(
m
)}() instead.`
);
};
});

// CURSORS

// There are several classes which relate to cursors:
Expand Down Expand Up @@ -868,6 +881,12 @@ Cursor.prototype.countAsync = async function () {
);
};

Cursor.prototype.count = function () {
throw new Error(
"count() is not avaible on the server. Please use countAsync() instead."
);
};

[...ASYNC_CURSOR_METHODS, Symbol.iterator, Symbol.asyncIterator].forEach(methodName => {
// count is handled specially since we don't want to create a cursor.
// it is still included in ASYNC_CURSOR_METHODS because we still want an async version of it to exist.
Expand Down
40 changes: 26 additions & 14 deletions packages/mongo/remote_collection_driver.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
ASYNC_COLLECTION_METHODS,
getAsyncMethodName
getAsyncMethodName,
CLIENT_ONLY_METHODS
} from "meteor/minimongo/constants";

MongoInternals.RemoteCollectionDriver = function (
Expand Down Expand Up @@ -30,22 +31,33 @@ Object.assign(MongoInternals.RemoteCollectionDriver.prototype, {
open: function (name) {
var self = this;
var ret = {};
REMOTE_COLLECTION_METHODS.forEach(
function (m) {
ret[m] = _.bind(self.mongo[m], self.mongo, name);
REMOTE_COLLECTION_METHODS.forEach(function (m) {
ret[m] = _.bind(self.mongo[m], self.mongo, name);

if (!ASYNC_COLLECTION_METHODS.includes(m)) return;
const asyncMethodName = getAsyncMethodName(m);
ret[asyncMethodName] = function (...args) {
try {
return Promise.resolve(ret[m](...args));
} catch (error) {
return Promise.reject(error);
}
if (!ASYNC_COLLECTION_METHODS.includes(m)) return;
const asyncMethodName = getAsyncMethodName(m);
ret[asyncMethodName] = function (...args) {
try {
return Promise.resolve(ret[m](...args));
} catch (error) {
return Promise.reject(error);
}
});
};
});

CLIENT_ONLY_METHODS.forEach(function (m) {
ret[m] = _.bind(self.mongo[m], self.mongo, name);

ret[m] = function (...args) {
throw new Error(
`${m} + is not available on the server. Please use ${getAsyncMethodName(
m
)}() instead.`
);
};
});
return ret;
}
},
});

// Create the singleton RemoteCollectionDriver only on demand, so we
Expand Down

0 comments on commit eeaa406

Please sign in to comment.