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

Handle missing files during an export and open #566

Merged
merged 7 commits into from
Oct 28, 2019
Merged

Conversation

sssoleileraaa
Copy link
Contributor

Description

Tells the user that the file they're trying to export is missing from the file system. Next step is to show a Download link once again for the file that no longer exists on the filesystem, captured in this issue: #565

@eloquence eloquence changed the title handle missing file case Handle missing files during an export attempt Oct 11, 2019
@sssoleileraaa
Copy link
Contributor Author

Note: this fixes an unhandled FileNotFoundError which crashes the app

Copy link
Contributor

@redshiftzero redshiftzero left a comment

Choose a reason for hiding this comment

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

this is nice! one other thought: we might want to mark the file as not downloaded such that the user could then redownload the file (right now it's still marked as downloaded in the database so the download button isn't shown)

securedrop_client/logic.py Outdated Show resolved Hide resolved
securedrop_client/logic.py Outdated Show resolved Hide resolved
@sssoleileraaa
Copy link
Contributor Author

I had to rebase due to conflicts. I addressed the PR comment about displaying the "Download" link when the file is missing during either an open or export operation. I also added some cleanup code after export using the new export_usb_call_success signal which I was able to access after rebasing. I still need to update tests but will ask for a new review once ready. We are tracking clean up for the open function in this Issue: #579 (comment)

@sssoleileraaa
Copy link
Contributor Author

Finished writing unit tests. For manual testing, here's what you should do:

Regression Test

  1. Send a file as a source.
  2. Download the file, see that it exists in the data directory.
  3. Open the file.
  4. Close out of the display VM.
  5. Open the file again.
  6. Do the same steps but for export.

Testing new behavior for File Open

  1. Send a file as a source.
  2. Download the file, see that it exists in the data directory.
  3. Delete the file from the data directory using rm
  4. Open the file and see error message and wait about 10 seconds for the api sync to finish before displaying the "Download" link next to file again.
  5. Download the file and open.
  6. Notice the file with the original filename still exists in the data directory, which is fine for now and being tracked in a separate issue.

Testing new behavior for File Export

  1. Send a file as a source.
  2. Download the file, see that it exists in the data directory.
  3. Delete the file from the data directory using rm
  4. Export the file and see error message and wait about 10 seconds for the api sync to finish before displaying the "Download" link next to file again.
  5. Download the file and export successfully.
  6. Notice the file with the original filename no longer exists in the data directory.

@redshiftzero redshiftzero self-requested a review October 22, 2019 14:25
@sssoleileraaa sssoleileraaa changed the title Handle missing files during an export attempt Handle missing files during an export and open Oct 22, 2019
Copy link
Contributor

@redshiftzero redshiftzero left a comment

Choose a reason for hiding this comment

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

this looks great! from some testing in Qubes:

✅ Regression test: Opening multiple times in dispVM success
✅ Regression test: Export multiple times to USB drive success
✅ New behavior: after file is deleted, after open attempt an error message is shown and on the next sync, download link appears (we'll need to eventually update this file widget via a signal but let's leave this for a followup)
✅ New behavior: after file is deleted and download link appears, then file can be redownloaded and opened successfully
✅ New behavior: after file is deleted and export is clicked, after export an error message is shown and on next sync the download link appears

⚠️ but: the ExportDialog is not closed when the error message is shown

✅ New behavior: after file is deleted and download link appears, then file can be redownloaded and exported successfully

storage.mark_as_not_downloaded(file_uuid, self.session)
self.sync_api()
logger.debug(msg)
self.gui.update_error_status(msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

hmm so we have this same stanza in three places (almost) repeated other than the msg having "export" or "open" - what do you think about adding another method for this logic called e.g. _check_if_file_exists? I think it would also make reading this function a bit clearer. We could also make the error message generic so we don't even need to pass in the operation: "Could not complete action on file {}. File does not exist"

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 like your _check_if_file_exists suggestion since we use it in three places: opening a file, preflight check, and exporting a 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.

actually thinking about this more, it would make more sense to verify the file system accurately reflects the local db during a sync rather than whenever the user tries to access the file. i'll modify the code so that we:

  • check that the file exists and maybe use https://pypi.org/project/filelock/ so make sure it can't be accessed during open or export
  • mark the file to be redownloaded if it's missing during a sync

storage.mark_as_not_downloaded(file_uuid, self.session)
self.sync_api()
logger.debug(msg)
self.gui.update_error_status(msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

the other thing we need to decide is how to close the export dialog in the file not found case. some options:

  1. Add a little method on the Export object that closes the ExportDialog, which we can call from the controller. This would work, but I don't love this approach because there already is a method for handling errors in the export process which is via the pre_flight and usb failure signals.
  2. At this failure stage, emit the pre_flight or usb export failure signal and modify ExportDialog._update to handle a new error status: when the file is not there. The ExportDialog can just close in this case because we are showing the error in the main window.
  3. Move this file not found logic to before the export dialog even pops up.

I'm a fan of either 2 or 3. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, right now, we just wait for the gui to refresh before the dialog closes when the file is missing. i think using the failure signal makes the most sense since the file could be deleted while the dialog is open so it would be good to be able to have code in place to close the dialog upon a file missing error.

@sssoleileraaa
Copy link
Contributor Author

⚠️ but: the ExportDialog is not closed when the error message is shown

this has been fixed and now syncing includes marking files as not downloaded if they are missing from the file system. this is useful if say a user decided to move a bunch of files in the data directory (people working in qubes often run linux commands so this is plausible but user testing will be helpful here) and now we will set them all to be marked as not downloaded rather than waiting unti lthe user tries to open one.

Copy link
Contributor

@redshiftzero redshiftzero left a comment

Choose a reason for hiding this comment

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

lgtm! Retested the export scenarios with the latest changes: the export UI does not appear in the file missing case and export to USB occurs without issue once the file is redownloaded

@@ -1799,6 +1799,10 @@ def _on_export_clicked(self):
"""
Called when the export button is clicked.
"""
if not self.controller.downloaded_file_exists(self.file.uuid):
self.controller.sync_api()
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need to resync the API? we could just update the file with is_downloaded=False and then update the UI. I'll file a followup for this though since imho we'll should do this via sending a signal to FileWidget which is a larger change

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 think maybe we could do both? Let's discuss in the followup ticket you opened.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants