Skip to content
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

Making mongo errors clearer #12802

Merged
merged 14 commits into from
Oct 6, 2023
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