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
Editorial workflow dependency tracking #243
Editorial workflow dependency tracking #243
Conversation
10747bc
to
071e956
Compare
src/reducers/editorialWorkflow.js
Outdated
@@ -76,6 +77,15 @@ const unpublishedEntries = (state = null, action) => { | |||
// Update Optimistically | |||
return state.deleteIn(['entities', `${ action.payload.collection }.${ action.payload.slug }`]); | |||
|
|||
case UNPUBLISHED_ENTRY_REGISTER_DEPENDENCY: | |||
return state.setIn([ |
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 is currently storing the dependencies in entry.data.dependencies
, where it is then grabbed in persistUnpublishedEntry
, added to options
, and stored in metadata in editorialWorkflowGit
. Should this be saving directly to entry.metaData
instead?
8735aaa
to
24d5dfc
Compare
example/config.yml
Outdated
@@ -1,58 +1,29 @@ | |||
backend: | |||
name: test-repo |
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.
please, do not push changes to this file
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 is temporary, it'll be rebased out before this leaves WIP.
src/actions/editorialWorkflow.js
Outdated
}; | ||
} | ||
|
||
function unpublishedEntryDependenciesRequest (collection, slug) { |
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.
please, remove space between method name and parameters.
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.
Will do (ditto to those below).
src/actions/editorialWorkflow.js
Outdated
}; | ||
} | ||
|
||
function unpublishedEntryDependenciesSuccess (collection, slug, dependencies) { |
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.
please, remove space between method name and parameters.
src/actions/editorialWorkflow.js
Outdated
}; | ||
} | ||
|
||
function unpublishedEntryDependenciesError (collection, slug, error) { |
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.
please, remove space between method name and parameters.
src/actions/editorialWorkflow.js
Outdated
|
||
export const registerUnpublishedEntryDependency = unpublishedEntryRegisterDependency; | ||
|
||
export function getUnpublishedEntryDependencies (collection, slug) { |
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.
please, remove space between method name and parameters.
src/actions/editorialWorkflow.js
Outdated
"dependencies", | ||
]; | ||
|
||
const getEventualDependencies = (paths, loadedDeps, state) => |
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.
why is this function defined inside the parent function? please, extract.
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 function is only useful within getUnpublishedEntryDependencies
- I didn't want it exposed to the outer scope since it's very specific to the functionality of traverse
. I'm fine with extracting it, that was just the reasoning behind nesting it with traverse
.
src/actions/editorialWorkflow.js
Outdated
.then(() => path); | ||
}); | ||
|
||
const traverse = (collectedDeps, path) => { |
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.
why is this function defined inside the parent function? please, extract.
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 function closes over getState
, which is why it was defined here. I can hoist it and make getState
an argument, though.
src/actions/editorialWorkflow.js
Outdated
}; | ||
|
||
// Begin traversal | ||
return traverse(new Immutable.List(), `${ collection }.${ slug }`) |
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.
If you use a Map rather than a List, you'll avoid the O(N) lookup per iteration in the traverse
call.
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.
Good point, thanks!
24d5dfc
to
9faf2ed
Compare
@calavera I've addressed your comments on |
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.
Finally got some time to go through this. Still want to see if I can find some time to dig a little more into the traverse
function - it feels like it could be cleaned up a bit, but I would have to spend a bit more understanding it to get a closer look.
Apart from some minor things, the main issue I see is keeping the dependency state in in entry.data.dependencies
, when entry.data
should be the actual data that will be stored in git when persisting the entry.
Apart from that a widget would also need to be able to remove or change a dependency - and I'm wondering if there's any good way to handle cases with a list of relation widgets (what happens when a list item is removed).
src/actions/editorialWorkflow.js
Outdated
const state = getState(); | ||
|
||
if (collectedDeps.get(path) === true) { | ||
return new Promise(resolve => resolve(collectedDeps)); |
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.
return Promise.resolve(collectedDeps)
is cleaner
src/actions/editorialWorkflow.js
Outdated
if (!state.editorialWorkflow.hasIn(depsPath) || | ||
state.editorialWorkflow.getIn(depsPath).size === 0) { | ||
// This path has no dependencies | ||
return new Promise(resolve => resolve(newDeps)); |
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.
return Promise.resolve(newDeps)
src/reducers/entryDraft.js
Outdated
@@ -73,6 +74,10 @@ const entryDraftReducer = (state = Map(), action) => { | |||
case REMOVE_ASSET: | |||
return state.update('mediaFiles', list => list.filterNot(path => path === action.payload)); | |||
|
|||
case UNPUBLISHED_ENTRY_REGISTER_DEPENDENCY: | |||
return state.setIn(['entry', 'data', 'dependencies', action.payload.field], |
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 entry data is what gets persisted in the repository - we don't want to add "dependencies" to the frontmatter of the entry and the name could easily class with a field.
Either we should store them in something like entry.dependencies
or do like the assets and have dependencies
in the state for entryDraft.
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.
Ah, thanks for clarifying that. We definitely don't want this clashing with frontmatter. I'll also change the code in src/backends/backend.js/persistEntry
to pass the dependencies somewhere other than parsedData
, for the same reason (perhaps in the options
object).
Touching on that last point, I was originally passing the deps into the backend in persistUnpublishedEntry
, but it seemed that it wasn't getting called when creating a new unpublished entry, so dependencies that were created when the entry was first created weren't being saved. Is this behavior of persistUnpublishedEntry
intentional or a bug?
src/components/Widgets/ControlHOC.js
Outdated
return React.createElement(controlComponent, { | ||
field, | ||
value, | ||
metadata, | ||
onChange, | ||
onAddAsset, | ||
onRemoveAsset, | ||
registerDependency, |
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.
seems like onAddDependency
and onRemoveDependency
would be more in line with the other callback functions in terms of naming (and it should be possible to remove a dependency as well as adding one...)
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.
Yeah, that's a good point re: naming. See my comment below for more on the removal and updating.
@biilmann updating a dependency already works (in the case of a 1-to-1 field->dependency relationship; there isn't any functionality to handle a 1-to-many field->dependencies relationship). Removing a dependency is unimplemented - perhaps this should just be triggered whenever a relation field changes (other than selecting a suggestion)? |
9faf2ed
to
abb067c
Compare
@biilmann I've pushed changes that address your comments. I've moved the dependency storage to I'm working on hooking this into the |
I think firing the publish request when the we're sure everything is correct might be more expected. @biilmann wdyt? |
1dc91cc
to
eca9c97
Compare
@calavera @biilmann I've pushed an updated version with all the functionality, including dependency removal. It currently uses |
ea55a23
to
291eb13
Compare
@calavera @biilmann I've clarified I've also rebased this onto the latest master. |
291eb13
to
65595a6
Compare
LGTM, but unfortunately, there are some conflicts. |
@calavera I'll get it rebased again. |
65595a6
to
a1a8096
Compare
Blech, tried to git in the morning and ended up accidentally squashing two commits. It looks like all the changes are still there, but the changes ended up in two commits instead of the four I had before. It is rebased onto the current master now, though. |
- New actions, `UNPUBLISHED_ENTRY_REGISTER_DEPENDENCY` and `UNPUBLISHED_ENTRY_UNREGISTER_DEPENDENCY`, in `src/actions/editorialWorkflow.js`, with exported action creators. - New reducers case in `src/reducers/entryDraft.js` for (un)registering dependencies to the current `entryDraft.entry`. - `persistEntry` in `src/backends/backend.js` pulls dependency data out of `entryDraft` and saves it to metadata. - New widget control props, `onAddDependency` and `onRemoveDependency` passed down to widget controls from `EntryPageHOC`. These call `(un)registerUnpublishedEntryDependency`. - `RelationControl` calls `onAddDependency` when selecting a suggestion.
- New action constants in `src/actions/editorialWorkflow.js` - `UNPUBLISHED_ENTRY_DEPENDENCIES_REQUEST`, `UNPUBLISHED_ENTRY_DEPENDENCIES_SUCCESS`, and `UNPUBLISHED_ENTRY_DEPENDENCIES_FAILURE`, with exported thunk action creators. - `loadUnpublishedEntry` returns its promise so it can be used to load dependencies. - Add new thunk action `getUnpublishedEntryDependencies` to resolve the dependencies of a given entry.
- New action constants in `src/editorialWorkflow.js` - `UNPUBLISHED_ENTRIES_PUBLISH_REQUEST`, `UNPUBLISHED_ENTRIES_PUBLISH_SUCCESS`, and `UNPUBLISHED_ENTRIES_PUBLISH_FAILURE`, with exported thunk action creators. - New backend and GitHub API function `publishUnpublishedEntries`. - New reducer case in `src/reducers/editorialWorkflow.js` which updates editorialWorkflow state when publishing multiple entries.
a1a8096
to
d73a80d
Compare
d73a80d
to
f74b719
Compare
I'm closing this PR for now, since it's quite old and not ready for release. I'll reopen this once work starts on it again, but I don't want anyone mistakenly reviewing what I have here since it needs to be rebased and I'm not currently working on it. |
- Summary
Dependency tracking for unpublished entries: closes #192.
- Test plan
benaiah/netlify-cms-test-repo
.- Description for the changelog
- A picture of a cute animal (not mandatory but encouraged)