Refactor the restore backup implementation to RestoreBackupJob

This commit is contained in:
Nino van Hooff 2020-03-10 14:21:52 +01:00
parent ed5c2b3f43
commit ebfad16508
3 changed files with 106 additions and 63 deletions

View file

@ -11,6 +11,7 @@ from UM.Signal import Signal, signalemitter
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
from cura.CuraApplication import CuraApplication
from plugins.CuraDrive.src.RestoreBackupJob import RestoreBackupJob
from plugins.Toolbox.src.UltimakerCloudScope import UltimakerCloudScope
from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest
@ -27,7 +28,6 @@ class DriveApiService:
"""The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling."""
BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL)
DISK_WRITE_BUFFER_SIZE = 512 * 1024
restoringStateChanged = Signal()
"""Emits signal when restoring backup started or finished."""
@ -82,61 +82,16 @@ class DriveApiService:
# If there is no download URL, we can't restore the backup.
return self._emitRestoreError()
def finishedCallback(reply: QNetworkReply, bu=backup) -> None:
self._onRestoreRequestCompleted(reply, None, bu)
restore_backup_job = RestoreBackupJob(backup)
restore_backup_job.finished.connect(self._onRestoreFinished)
restore_backup_job.start()
HttpRequestManager.getInstance().get(
url = download_url,
callback = finishedCallback,
error_callback = self._onRestoreRequestCompleted
)
def _onRestoreRequestCompleted(self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None, backup = None):
if not HttpRequestManager.replyIndicatesSuccess(reply, error):
Logger.log("w",
"Requesting backup failed, response code %s while trying to connect to %s",
reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url())
self._emitRestoreError()
return
# We store the file in a temporary path fist to ensure integrity.
temporary_backup_file = NamedTemporaryFile(delete = False)
with open(temporary_backup_file.name, "wb") as write_backup:
app = CuraApplication.getInstance()
bytes_read = reply.read(DriveApiService.DISK_WRITE_BUFFER_SIZE)
while bytes_read:
write_backup.write(bytes_read)
bytes_read = reply.read(DriveApiService.DISK_WRITE_BUFFER_SIZE)
app.processEvents()
if not self._verifyMd5Hash(temporary_backup_file.name, backup.get("md5_hash", "")):
# Don't restore the backup if the MD5 hashes do not match.
# This can happen if the download was interrupted.
Logger.log("w", "Remote and local MD5 hashes do not match, not restoring backup.")
return self._emitRestoreError()
# Tell Cura to place the backup back in the user data folder.
with open(temporary_backup_file.name, "rb") as read_backup:
self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("metadata", {}))
self.restoringStateChanged.emit(is_restoring = False)
def _emitRestoreError(self) -> None:
self.restoringStateChanged.emit(is_restoring = False,
error_message = catalog.i18nc("@info:backup_status",
"There was an error trying to restore your backup."))
@staticmethod
def _verifyMd5Hash(file_path: str, known_hash: str) -> bool:
"""Verify the MD5 hash of a file.
:param file_path: Full path to the file.
:param known_hash: The known MD5 hash of the file.
:return: Success or not.
"""
with open(file_path, "rb") as read_backup:
local_md5_hash = base64.b64encode(hashlib.md5(read_backup.read()).digest(), altchars = b"_-").decode("utf-8")
return known_hash == local_md5_hash
def _onRestoreFinished(self, job: "RestoreBackupJob"):
if job.restore_backup_error_message != "":
# If the job contains an error message we pass it along so the UI can display it.
self.restoringStateChanged.emit(is_restoring=False)
else:
self.restoringStateChanged.emit(is_restoring = False, error_message = job.restore_backup_error_message)
def deleteBackup(self, backup_id: str, finishedCallable: Callable[[bool], None]):
@ -153,5 +108,6 @@ class DriveApiService:
scope= self._jsonCloudScope
)
def _onDeleteRequestCompleted(self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None, callable = None):
@staticmethod
def _onDeleteRequestCompleted(reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None, callable = None):
callable(HttpRequestManager.replyIndicatesSuccess(reply, error))