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

feat: define collections using registerPlugin #5196

Merged
merged 35 commits into from
Jun 10, 2019

Conversation

aldeed
Copy link
Contributor

@aldeed aldeed commented Jun 3, 2019

Epic #4996
Resolves #4268
Impact: minor
Type: feature

Issue

Most MongoDB collections are defined in a single place in core code, breaking the plugin pattern. Some newer collections are defined in plugin startup functions, but this pattern does not give good control over when the collections exist. Ideally they should already be available when all plugin startup functions run.

Solution

Introduces a new collections option for registerPlugin, which allows plugins to define their MongoDB collections and indexes in a standard way, such that they'll exist already when plugin startup functions run.

Breaking changes

None. Custom plugins may still define collections in a startup function, but you are encouraged to move to the new pattern.

Additional changes

  • The TestApp class was duplicating several blocks of code from ReactionNodeApp class. Now, TestApp wraps and calls through to a ReactionNodeApp instance.
  • The collection changes caused timing failures in Meteor app tests. I added Reaction.onAppStartupComplete as a better way to stop all tests from running until after all collections are available and all startup code has run.
  • In order for integration tests to continue working, I had to move all plugins with collections to the newer non-Meteor registerPlugin pattern.
  • The collection pattern changes added to startup confusion around file-collections startup. To resolve this, I moved around some files plugin code and eliminated some code that was duplicated between non-Meteor and Meteor startup. None of the actual files code logic is changed; it's just called in better places and in a clearer order.

Testing

  • Smoke test the API and verify that everything continues to work.
  • Smoke test the Catalyst UI and verify that everything continues to work.
  • Smoke test the example storefront connected to the API in this branch and verify that everything continues to work.

aldeed added 28 commits June 2, 2019 10:51
plugin register

Signed-off-by: Eric Dobbertin <[email protected]>
Signed-off-by: Eric Dobbertin <[email protected]>
Signed-off-by: Eric Dobbertin <[email protected]>
for ReactionNodeApp

Signed-off-by: Eric Dobbertin <[email protected]>
Signed-off-by: Eric Dobbertin <[email protected]>
to fix duplicate loading when running within Meteor

Signed-off-by: Eric Dobbertin <[email protected]>
@aldeed aldeed self-assigned this Jun 3, 2019
@aldeed aldeed requested a review from mikemurray June 3, 2019 20:03
@aldeed aldeed requested a review from brent-hoover June 3, 2019 20:06
@aldeed
Copy link
Contributor Author

aldeed commented Jun 3, 2019

Reviewer note: Unfortunately this was the type of change that cascaded into needing several changes that I couldn't pull apart. The diff looks worse than it is. All the register.js files are just copy/paste from the Meteor file into the non-Meteor file, plus add collections option. In many cases the collections were moved out of startup.js, so that means all of those files changed, too. Beyond that, most changes were to get tests passing again.

Copy link
Collaborator

@brent-hoover brent-hoover left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a couple of questions and notes but nothing blocking.

@@ -74,7 +79,32 @@ export default class ReactionNodeApp {

setMongoDatabase(db) {
this.db = db;
defineCollections(this.db, this.collections);

for (const pluginName in this.registeredPlugins) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could definitely use some comments to break up what's happening

}
this.collections[collectionKey] = this.db.collection(collectionConfig.name);
if (Array.isArray(collectionConfig.indexes)) {
for (const indexArgs of collectionConfig.indexes) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious why you chose to use the for over using something like Array.forEach. It seems like generally we prefer the more javascript idomatic Array.forEach?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember why. I may have initially been awaiting the collectionIndex call?

indexes: [
// Create indexes. We set specific names for backwards compatibility
// with indexes created by the aldeed:schema-index Meteor package.
[{ groups: 1 }, { name: "c2_groups" }],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there no way to not use these old index names?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's a way to change index names, I suppose we could do a migration. But without that, if you leave out the name and they have the old names already in the db, you get an error about trying to create the same index with a different name. There may be some way, but it would take experimentation.

@@ -0,0 +1,28 @@
import startup from "./startup";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an aside that if I was to nominate a plugin to be removed this would def be one of them

@@ -0,0 +1,62 @@
import mutations from "./mutations";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like moving these to no-meteor/register is confusing. Does moving it to this directory enable any different functionality or is it just a mnemonic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mnemonic, but the new way of exporting a single register function that accepts app argument is important. It's a stepping stone toward NPM packages. Imagine the next step where everything in no-meteor moves to a separate package. Then the original, outer register.js file will import register from "@reactioncommerce/plugin-email/register"; and pass it the app to connect it.

The step after that is removing the Meteor auto-register stuff and having people explicitly call the plugins in the order they wish, like is done in registerPlugins.js. That ultimately will be the pattern when the API is fully transitioned to pure Node app.

@@ -8,6 +6,9 @@ import { Media } from "/imports/plugins/core/files/server";
* @return {String} Absolute image URL
*/
export default async function getShopLogo(context, shop) {
const { collections } = context;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could these be combined into one line? I don't see us using collections anywhere else

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I do this by habit since we have other places where we need it.


WebApp.connectHandlers.use("/assets/files", downloadManager.connectHandler);
WebApp.connectHandlers.use("/assets/uploads", (req, res) => {
req.baseUrl = "/assets/uploads"; // tus relies on this being set, which is an Express thing
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tus?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://tus.io/

It's what file-collections package uses for uploading.

@aldeed aldeed mentioned this pull request Jun 4, 2019
45 tasks
Copy link
Member

@manueldelreal manueldelreal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

Copy link
Member

@mikemurray mikemurray left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aldeed I'm seeing this error when I go to http://localhost:3000. Seems like the build started breaking after the merge from develop.

withPrimaryShopId.js:30 Uncaught TypeError: Cannot destructure property `primaryShopId` of 'undefined' or 'null'.
    at withPrimaryShopId.js:30
    at Query.render (modules.js?hash=8da96538218792e3708caf7d13f2ced0f2877951:287545)
    at finishClassComponent (modules.js?hash=8da96538218792e3708caf7d13f2ced0f2877951:193906)
    at updateClassComponent (modules.js?hash=8da96538218792e3708caf7d13f2ced0f2877951:193868)
    at beginWork (modules.js?hash=8da96538218792e3708caf7d13f2ced0f2877951:194537)
    at performUnitOfWork (modules.js?hash=8da96538218792e3708caf7d13f2ced0f2877951:196576)
    at workLoop (modules.js?hash=8da96538218792e3708caf7d13f2ced0f2877951:196615)
    at HTMLUnknownElement.callCallback (modules.js?hash=8da96538218792e3708caf7d13f2ced0f2877951:180812)
    at Object.invokeGuardedCallbackDev (modules.js?hash=8da96538218792e3708caf7d13f2ced0f2877951:180850)
    at invokeGuardedCallback (modules.js?hash=8da96538218792e3708caf7d13f2ced0f2877951:180899)

@brent-hoover
Copy link
Collaborator

Not to clutter this up since this is already a big one, but having given it some more thought I am thinking we should migrate these indexes to a standard naming convention now.

It could also be a separate PR but I think if we don't do it now I feel like we will never do it.

I am not blocking this PR for that but it is my suggestion.

@aldeed
Copy link
Contributor Author

aldeed commented Jun 7, 2019

@zenweasel That makes sense to me. I'll look into it for either this PR or another.

@aldeed
Copy link
Contributor Author

aldeed commented Jun 9, 2019

@mikemurray After another merge from develop and a few random other fixes, I'm not seeing the error you mention.

@mikemurray
Copy link
Member

@aldeed 👍 I will run it again

Copy link
Member

@mikemurray mikemurray left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aldeed Still getting a white screen and the error I posted above. Purged, cleaned, downed, restarted, but nothing. Could there be some configuration I'm missing?

In terminal I'm also seeing issues with migrations when sharting up:

reaction_1  | 20:59:56.493Z ERROR Reaction: Cannot destructure property `Accounts` of 'undefined' or 'null'.
reaction_1  |   TypeError: Cannot destructure property `Accounts` of 'undefined' or 'null'.
reaction_1  |       at Promise.asyncApply (imports/plugins/core/accounts/server/no-meteor/util/addRolesToGroups.js:75:3)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  |    => awaited here:
reaction_1  |       at Function.Promise.await (/home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
reaction_1  |       at Promise.asyncApply (imports/plugins/core/accounts/server/no-meteor/util/addRolesToGroups.js:58:3)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  |    => awaited here:
reaction_1  |       at Function.Promise.await (/home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
reaction_1  |       at Promise.asyncApply (imports/plugins/core/accounts/server/no-meteor/startup.js:171:5)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  |    => awaited here:
reaction_1  |       at Function.Promise.await (/home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
reaction_1  |       at Promise.asyncApply (imports/node-app/core/ReactionNodeApp.js:187:9)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  |    => awaited here:
reaction_1  |       at Function.Promise.await (/home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
reaction_1  |       at Promise.asyncApply (imports/plugins/core/core/server/startup/startNodeApp.js:68:5)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  | 20:59:56.629Z ERROR Reaction:
reaction_1  |   Error in migration 24, publishProductsToCatalog TypeError: Cannot read property 'findOne' of undefined
reaction_1  |       at Promise.asyncApply (imports/plugins/core/catalog/server/no-meteor/utils/publishProductToCatalogById.js:13:25)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  |    => awaited here:
reaction_1  |       at Function.Promise.await (/home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
reaction_1  |       at Promise.asyncApply (imports/plugins/core/catalog/server/no-meteor/utils/publishProductsToCatalog.js:13:16)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  |    => awaited here:
reaction_1  |       at Function.Promise.await (/home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
reaction_1  |       at Object.up (imports/plugins/core/versions/server/migrations/24_publish_all_existing_visible_products.js:19:17)
reaction_1  |       at migrate (packages/percolate_migrations.js:233:25)
reaction_1  |       at Object.Migrations._migrateTo (packages/percolate_migrations.js:253:7)
reaction_1  |       at Object.Migrations.migrateTo (packages/percolate_migrations.js:167:10)
reaction_1  |       at appEvents.on (imports/plugins/core/versions/server/startup.js:44:9)
reaction_1  |       at Promise.asyncApply (imports/node-app/core/util/appEvents.js:24:11)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  | 20:59:56.637Z ERROR Reaction:
reaction_1  |   Error while migrating TypeError: Cannot read property 'find' of undefined
reaction_1  |       at Promise.asyncApply (imports/plugins/core/versions/server/no-meteor/util/findAndConvertInBatches.js:21:18)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  |    => awaited here:
reaction_1  |       at Function.Promise.await (/home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/promise_server.js:56:12)
reaction_1  |       at Object.up (imports/plugins/core/versions/server/migrations/28_add_hash_to_products.js:11:5)
reaction_1  |       at migrate (packages/percolate_migrations.js:233:25)
reaction_1  |       at Object.Migrations._migrateTo (packages/percolate_migrations.js:253:7)
reaction_1  |       at Object.Migrations.migrateTo (packages/percolate_migrations.js:167:10)
reaction_1  |       at appEvents.on (imports/plugins/core/versions/server/startup.js:44:9)
reaction_1  |       at Promise.asyncApply (imports/node-app/core/util/appEvents.js:24:11)
reaction_1  |       at /home/node/.meteor/packages/promise/.0.11.2.1gwulfb.vjny++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/meteor-promise/fiber_pool.js:43:40
reaction_1  | Note: you are using a pure-JavaScript implementation of bcrypt.
reaction_1  | While this implementation will work correctly, it is known to be
reaction_1  | approximately three times slower than the native implementation.
reaction_1  | In order to use the native implementation instead, run
reaction_1  |
reaction_1  |   meteor npm install --save bcrypt

Copy link
Member

@mikemurray mikemurray left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it to work, and it looks good.

@mikemurray mikemurray merged commit 860dec0 into develop Jun 10, 2019
@mikemurray mikemurray deleted the feat-aldeed-4268-collections-config branch June 10, 2019 22:16
@jeffcorpuz jeffcorpuz mentioned this pull request Jul 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants