-
Notifications
You must be signed in to change notification settings - Fork 615
PRO-6472: at startup, automatically supply a value for any new schema property that has a def or fallback def #4721
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
Conversation
index.js
Outdated
| self.apos.schema.registerAllSchemas(); | ||
| await self.apos.lock.withLock('@apostrophecms/migration:migrate', async () => { | ||
| await self.apos.migration.migrate(); // emits before and after events, inside the lock | ||
| await self.apos.migration.migrate(self.apos.argv); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency as this was passed in the other, legacy invocation. It is not currently used.
| self.apos.schema.registerAllSchemas(); | ||
| await self.apos.lock.withLock('@apostrophecms/migration:migrate', async () => { | ||
| await self.apos.migration.migrate(); // emits before and after events, inside the lock | ||
| await self.apos.migration.migrate(self.argv); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency, it was passed in the other, redundant invocation.
| for (const key of Object.keys(step)) { | ||
| if (!(validKeys.includes(key) || cascades.includes(key))) { | ||
| const message = upgradeHints[key] || `${key} is not a valid top level property for an Apostrophe 3.x module. Make sure you nest regular module options in the new "options" property.`; | ||
| const message = upgradeHints[key] || `${key} is not a valid top level property for an Apostrophe module. Make sure you nest regular module options in the "options" property.`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated language.
| }); | ||
| }); | ||
| }, | ||
| async executeMigrations() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
D'oh. This was completely redundant in the current setup.
| } | ||
| } | ||
| }, | ||
| before: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invoke the new capability, which is not a registered, named migration, but rather an independent part of the migration phase that does its own checks to determine if work is needed.
| // Other migration-related facts that are not migration | ||
| // names are stored with a leading *, leave them alone | ||
| await self.db.removeMany({ | ||
| _id: /^[^*]/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somewhere to keep memos like the previous list of existing schema fields. Otherwise we have a proliferation of tiny mongo collections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could have just one new, separate collection like aposModuleSchema or aposRegisteredSchema so we don't store it into the aposMigration collection, which is a bit tied to migrations, but not really...
Inside that dedicated collection, we could have the schema stored in documents: 1 document in that collection = 1 module schema.
That would require changing a lot of code, and since you've already finished the feature, maybe it's not worth it.
Just wanted to have your thoughts on that, to have the schemas stored in a dedicated collection for clarity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather not proliferate collections for very small amounts of data unless necessary as they can add open file descriptors in MongoDB.
| // Migrations actually run on every invocation | ||
| // and automatically detect whether any work | ||
| // must be done | ||
| task: () => {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously this task invoked the migrate code three times in dev: once in the actual, intended migration phase of startup (see index.js), once in an "apostrophe:ready" handler, and once here. Being thorough I guess geez past Tom
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we remove the entire tasks block?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The task must exist otherwise users will get an error when launching the migration task according to our documentation. The fact that it is implicit is an implementation detail that should not concern users. Later we may eliminate the implicit invocation except when the task is being invoked, in production.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means that if I manually run node app.js @apostrophecms/migration:migrate nothing will happen now.
I might want to run the migration without starting apostrophe server, or start apostrophe without the migration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@haroun it has worked like this for about as long as A3 has existed. Plus extra, redundant invocations in two other places. Since we have this automatic invocation at every startup I am fixing the bug of redundant invocations. As I mentioned we might reconsider this behavior later for production, but in dev it never makes sense to start up without migrating, if your code does not behave after migrations then that is a bug in the migrations that needs attention and something that will break in production if bypassed, etc.
| return { | ||
| async addMissingSchemaFields() { | ||
| let scans = 0; let updates = 0; | ||
| const lastPropLists = await self.getLastPropLists(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A "prop list" is a list of all schema properties that existed for a doc type last time, for comparison with the new list for this startup to determine if any property additions might potentially be needed. We make this determination quickly and move on if new work is not needed.
| }, | ||
| expandPropList(schema, propList, dotPath) { | ||
| for (const field of schema) { | ||
| const newDotPath = dotPath ? `${dotPath}.${field.name}` : field.name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're not coming up with a real dot path to patch anything at this stage, just a conceptual dot path that namespaces these properties enough to determine if there are differences from one generation of code to the next by comparing two lists of paths.
Real dot paths get figured out in the guts of addMissingSchemaFieldsFor, which does the hard work if a difference does exist.
|
Please note I broke document versions out separately because it can be done at any point, ideally also this cycle, but nothing gets worse if we don't get to it this cycle: |
ETLaurent
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall logic lgtm
haroun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, we need to come up with a solution for the document-versions module.
Versions are stored in another collections.
If the field is required, we might want to add the def values or something (like for required fields without default value) during the restore method call.
| } | ||
| } | ||
| }, | ||
| ...require('./lib/addMissingSchemaFields.js')(self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we put the require at the top to make the dependency clear
const addMissingSchemaFields = require('./lib/addMissingSchemaFields.js');
and then
| ...require('./lib/addMissingSchemaFields.js')(self) | |
| ...addMissingSchemaFields(self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| // Migrations actually run on every invocation | ||
| // and automatically detect whether any work | ||
| // must be done | ||
| task: () => {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means that if I manually run node app.js @apostrophecms/migration:migrate nothing will happen now.
I might want to run the migration without starting apostrophe server, or start apostrophe without the migration.
| if (!_.isEqual(lastPropLists?.[name], propLists[name])) { | ||
| changesToPropLists = true; | ||
| scans++; | ||
| updates += await self.addMissingSchemaFieldsForDocType(name, propLists[name]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method accepts only 1 parameter below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The migration phase will run on any startup. Including an empty task. So omitting the call from the task is just eliminating redundancy.
At a future time we might reintroduce an optional behavior where the migration does not run in production except in the task, but that behavior has effectively not been possible before, so I don't have to introduce that feature to fix the redundancy bug.
* upstream/main: fix oversights in add missing schema fields (apostrophecms#4736) upgrate stylelint-config-apostrophe to 4.2.0 (apostrophecms#4735) PRO-6472: at startup, automatically supply a value for any new schema property that has a def or fallback def (apostrophecms#4721) fix sass warning (apostrophecms#4730) fix piecesFilters with dynamic choices (apostrophecms#4731) hotfix 4.7.1 (apostrophecms#4733) PRO-6591: parked fields overrides (apostrophecms#4732) do not let a page become a child of itself (apostrophecms#4726)
Summary
Summarize the changes briefly, including which issue/ticket this resolves. If it closes an existing Github issue, include "Closes #[issue number]"
What are the specific steps to test this change?
See tests.
What kind of change does this PR introduce?
(Check at least one)
Make sure the PR fulfills these requirements:
If adding a new feature without an already open issue, it's best to open a feature request issue first and wait for approval before working on it.
Other information: