diff --git a/packages/accounts-2fa/2fa-server.js b/packages/accounts-2fa/2fa-server.js index 45f701a2ee..188d829489 100644 --- a/packages/accounts-2fa/2fa-server.js +++ b/packages/accounts-2fa/2fa-server.js @@ -59,7 +59,7 @@ Meteor.methods({ }); const svg = new QRCode(uri).svg(); - await Meteor.users.update( + await Meteor.users.updateAsync( { _id: user._id }, { $set: { @@ -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: { @@ -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: { diff --git a/packages/accounts-password/email_tests.js b/packages/accounts-password/email_tests.js index afcdef3905..6c78320c58 100644 --- a/packages/accounts-password/email_tests.js +++ b/packages/accounts-password/email_tests.js @@ -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) { @@ -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); })); } ]); @@ -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) { @@ -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) { @@ -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) { @@ -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); })); } ]); diff --git a/packages/accounts-password/email_tests_setup.js b/packages/accounts-password/email_tests_setup.js index e06fbfa789..fe393fb663 100644 --- a/packages/accounts-password/email_tests_setup.js +++ b/packages/accounts-password/email_tests_setup.js @@ -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); diff --git a/packages/accounts-password/password_tests.js b/packages/accounts-password/password_tests.js index 5cf096a9da..6d80c80552 100644 --- a/packages/accounts-password/password_tests.js +++ b/packages/accounts-password/password_tests.js @@ -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); diff --git a/packages/accounts-password/password_tests_setup.js b/packages/accounts-password/password_tests_setup.js index 80964c72fd..48a34c8766 100644 --- a/packages/accounts-password/password_tests_setup.js +++ b/packages/accounts-password/password_tests_setup.js @@ -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: @@ -137,6 +137,6 @@ Meteor.methods( }, removeUser: - async username => await Meteor.users.remove({ "username": username }), + async username => await Meteor.users.removeAsync({ "username": username }), } ); diff --git a/packages/minimongo/constants.js b/packages/minimongo/constants.js index fc4c5b9225..3795a8dc68 100644 --- a/packages/minimongo/constants.js +++ b/packages/minimongo/constants.js @@ -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"]; diff --git a/packages/mongo/collection.js b/packages/mongo/collection.js index 73d8590563..6d2199965c 100644 --- a/packages/mongo/collection.js +++ b/packages/mongo/collection.js @@ -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, @@ -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); -// } -// }; -// }); diff --git a/packages/mongo/mongo_driver.js b/packages/mongo/mongo_driver.js index 0c2e021036..8fc3644b86 100644 --- a/packages/mongo/mongo_driver.js +++ b/packages/mongo/mongo_driver.js @@ -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"; @@ -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) { @@ -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: @@ -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. diff --git a/packages/mongo/remote_collection_driver.js b/packages/mongo/remote_collection_driver.js index c3954a9cf9..36a8a30910 100644 --- a/packages/mongo/remote_collection_driver.js +++ b/packages/mongo/remote_collection_driver.js @@ -1,6 +1,7 @@ import { ASYNC_COLLECTION_METHODS, - getAsyncMethodName + getAsyncMethodName, + CLIENT_ONLY_METHODS } from "meteor/minimongo/constants"; MongoInternals.RemoteCollectionDriver = function ( @@ -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