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

make changes for import/export module to work properly #4270

Merged
merged 26 commits into from
Oct 12, 2023

Conversation

ETLaurent
Copy link
Contributor

@ETLaurent ETLaurent commented Aug 15, 2023

PRO-4271

Summary

Brings some changes in core that are necessary for the new Import/Export module.
Must be tested with this PR.

  • Allows to insert attachment with a given ID (to import attachments with same IDs). As well as with docIds and archiveDocIds to preserve related docs.

  • Adds an update method to the attachment module in order to allow a user that choose to override a piece with attachment to delete the existing one and reinsert it (the document in DB and the associated file). It deletes and recreates to reuse existing code and avoid big changes here, since the insert method does a lot of things.

  • Adds an option to the http remote method to allow receiving the original response from node-fetch, it allows to manipulate the response as a stream before it has been consumed in the method by res.text() we need it to download images through streams and push them in the compressed file.

What are the specific steps to test this change?

See steps in this PR.

What kind of change does this PR introduce?

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Build-related changes
  • Other

Make sure the PR fulfills these requirements:

  • It includes a) the existing issue ID being resolved, b) a convincing reason for adding this feature, or c) a clear description of the bug it resolves
  • The changelog is updated
  • Related documentation has been updated
  • Related tests have been updated

@linear
Copy link

linear bot commented Aug 15, 2023

PRO-4261 As an editor, I can download a ZIP file of my exported documents

When clicking on the download button of the export modal, after I selected the related docs that I want to include in the export, the process must be triggered.

image.png

Two new routes export must exist in the modules piece-type and page. Both routes will call a method export from the module doc-type .

This method should take the IDs of the documents the user want to export, as well as the related types that he wants to get. Only the types, not the IDs, since we decided that it would be too heavy for the export modal to get the amount of related docs when we batch a large amount of documents.

Generate files

This method will need to use the relate method from the module schema, this method fills documents with related ones.

Since we need to store documents in a flat EJSON array, it might be a good idea to add an option to this method in order to get a flat array of related documents instead of filling requested documents with their related docs, see how it could potentially be used with query builders.

We want one EJSON file for each db collection, for example aposDocs.json and aposAttachment.json, containing arrays of documents..

We want, for each document to export the draft and live versions (only draft if there is no live). The draft version must appear first to avoid situation where a live exists without draft which is forbidden.

Same thing for related documents, they must be written first in the EJSON file, otherwise the doc manager will yell when we'll import it.

We will create an attachment folder when actual attachments will be stored (images, files…).

Generate Zip

To generate the ZIP file we would prefer using the native node library.

Progress bar notification

This progress bar notification is triggered by wrapping your export function with the run method of the job module. Here is an example from the piece-type-importer module:

return self.apos.modules['apostrophecms/job'].run(
  req,
  (req, reporting, { notificationId }) => self.importRun(req, {
    progressNotifId: notificationId,
    filePath: file.path,
    reporting,
    totalPieces
  }),
  {}
);

The reporting object passed from the second argument function allows to set the total of the documents you want to import with the method setTotal, as well as to call the methods success or failure for every document that we tried to export or import.

Finally the ZIP file will be sent back to the client and the download triggered from the user's browser.

Permissions

For exporting documents, a user that has at least one per-type permission should be able to export anything. He won’t be able to see adminOnly documents, therefore should not be able to export these documents. It matches the way it works currently because in this case the user can see all drafts. In core, admin should be able to export adminOnly content, except for users.

For Reference

https://github.com/apostrophecms/tech-designs/blob/main/3.x/share-documents-across-sites/design.md

Acceptance Criteria

  • I'm an editor or admin on with the Download modal open, and my documents selected, and I hit "Download" and when I do, a ZIP file begins downloading.
  • The zip file downloads and I open it and I see the the draft and live versions (only draft if there is no live) in my unarchived folder.

@ValJed ValJed force-pushed the pro-4261-export-docs branch from d5171df to 30ab5da Compare August 22, 2023 12:38
@ETLaurent ETLaurent changed the title pass checked document ids to toolbar make changes for import/export module to work properly Aug 29, 2023
@@ -432,7 +437,11 @@ module.exports = {
}
if (self.isSized(extension)) {
// For images we correct automatically for common file extension mistakes
const result = await Promise.promisify(self.uploadfs.copyImageIn)(file.path, '/attachments/' + info._id + '-' + info.name, { sizes: self.imageSizes });
const result = await Promise.promisify(self.uploadfs.copyImageIn)(
Copy link
Contributor

Choose a reason for hiding this comment

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

only indentation.

@ValJed ValJed requested a review from boutell October 3, 2023 12:42
@ValJed ValJed marked this pull request as ready for review October 3, 2023 12:42
Copy link
Member

@boutell boutell left a comment

Choose a reason for hiding this comment

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

Small things hopefully

@@ -402,16 +401,21 @@ module.exports = {
extensions: accepted.join(req.t('apostrophe:listJoiner'))
}));
}

if (options.attachmentId && await self.apos.attachment.db.findOne({ _id: options.attachmentId })) {
throw self.apos.error('invalid', 'duplicate');
Copy link
Member

Choose a reason for hiding this comment

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

This could hit a race condition and imposes an unnecessary delay, try/catch the insert and check for self.apos.doc.isUniqueError(err) to see if mongodb threw a unique key error or something else.

Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed I kept the behavior as it is for now, we might create a ticket to update slightly the insert method in order to insert the doc before to copy to file to uploadfs.
At least here if a user use the option attachmentId (I don't see a lot of use case) they are protected from a mess (file created while the document has not been inserted).

await self.db.deleteOne({ _id: existing._id });
await self.insert(req, file, {
attachmentId: attachment._id,
docIds: attachment.docIds,
Copy link
Member

Choose a reason for hiding this comment

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

Merge the old and new docIds and archivedDocIds so you have a complete list. Conversely, use $in queries with an { _id: 1 } projection to confirm they all actually exist on the receiving site and filter out those that don't. (I assume you're doing all of the attachments last after the docs so that this would give a complete result.)

Copy link
Member

Choose a reason for hiding this comment

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

I know we are pressed for time. There is also a much simpler alternative re: tracking docIds and archivedDocIds. Just use the recomputeAllDocReferences task programmatically at the end of the entire import process. It takes a little time, but and it will definitely get the right result without any tricky bookkeeping. It is 100% OK to do that for this first release at least, if you find it difficult to get the lists right per the technique above.

You can invoke a task internally but I'd just refactor its logic to a method for convenience (with no other changes).

Copy link
Contributor

@ValJed ValJed Oct 6, 2023

Choose a reason for hiding this comment

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

Thanks for having spotted that. I now only fill these arrays with existing docIds and archivedDocIds and the new ones that exist in the current site based on if the document is archived or not.

@@ -56,6 +56,7 @@
:labels="moduleLabels"
:disable="relationshipErrors === 'min'"
:displayed-items="items.length"
:checked="checked"
Copy link
Member

Choose a reason for hiding this comment

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

Huh, was this just an unrelated bug?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not a bug, it has been added to be consistent with the other vue changes.
Basically the modal for exporting documents needed the checked IDs so we pass it to AposDocsManagerToolbar when used.

@ValJed ValJed requested a review from boutell October 6, 2023 15:47
Copy link
Member

@boutell boutell left a comment

Choose a reason for hiding this comment

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

You also need to merge in the docIds and archivedDocIds from existing because the attachment could be present in other documents on the receiving site.

CHANGELOG.md Outdated
### Add

* Allows to insert attachments with a given ID, as well as with `docIds` and `atchiveDocIds` to preserve related docs.
* Adds an `update` method to the attachment module, that update the mongoDB doc and the associated file.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
* Adds an `update` method to the attachment module, that update the mongoDB doc and the associated file.
* Adds an `update` method to the attachment module, that updates the mongoDB doc and the associated file.

CHANGELOG.md Outdated

### Add

* Allows to insert attachments with a given ID, as well as with `docIds` and `atchiveDocIds` to preserve related docs.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
* Allows to insert attachments with a given ID, as well as with `docIds` and `atchiveDocIds` to preserve related docs.
* Allows to insert attachments with a given ID, as well as with `docIds` and `archiveDocIds` to preserve related docs.

@ETLaurent ETLaurent requested a review from boutell October 12, 2023 10:05
Comment on lines +481 to +484
...existing.docIds,
...existing.archivedDocIds,
...attachment.docIds,
...attachment.archivedDocIds
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@boutell is this what you meant?

Copy link
Member

Choose a reason for hiding this comment

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

Yes sorry

@ETLaurent ETLaurent merged commit 9ccb6be into main Oct 12, 2023
8 checks passed
@ETLaurent ETLaurent deleted the pro-4261-export-docs branch October 12, 2023 12:11
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.

3 participants