DriveApiService.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. # Copyright (c) 2018 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import Any, Optional, List, Dict, Callable
  4. from PyQt6.QtNetwork import QNetworkReply
  5. from UM.Logger import Logger
  6. from UM.Signal import Signal, signalemitter
  7. from UM.TaskManagement.HttpRequestManager import HttpRequestManager
  8. from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
  9. from UM.i18n import i18nCatalog
  10. from cura.CuraApplication import CuraApplication
  11. from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope
  12. from .CreateBackupJob import CreateBackupJob
  13. from .RestoreBackupJob import RestoreBackupJob
  14. from .Settings import Settings
  15. catalog = i18nCatalog("cura")
  16. @signalemitter
  17. class DriveApiService:
  18. """The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling."""
  19. BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL)
  20. restoringStateChanged = Signal()
  21. """Emits signal when restoring backup started or finished."""
  22. creatingStateChanged = Signal()
  23. """Emits signal when creating backup started or finished."""
  24. def __init__(self) -> None:
  25. self._cura_api = CuraApplication.getInstance().getCuraAPI()
  26. self._json_cloud_scope = JsonDecoratorScope(UltimakerCloudScope(CuraApplication.getInstance()))
  27. def getBackups(self, changed: Callable[[List[Dict[str, Any]]], None]) -> None:
  28. def callback(reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None:
  29. if error is not None:
  30. Logger.log("w", "Could not get backups: " + str(error))
  31. changed([])
  32. return
  33. backup_list_response = HttpRequestManager.readJSON(reply)
  34. if backup_list_response is None:
  35. Logger.error("List of back-ups can't be parsed.")
  36. changed([])
  37. return
  38. if "data" not in backup_list_response:
  39. Logger.log("w", "Could not get backups from remote, actual response body was: %s",
  40. str(backup_list_response))
  41. changed([]) # empty list of backups
  42. return
  43. changed(backup_list_response["data"])
  44. HttpRequestManager.getInstance().get(
  45. self.BACKUP_URL,
  46. callback= callback,
  47. error_callback = callback,
  48. scope=self._json_cloud_scope
  49. )
  50. def createBackup(self) -> None:
  51. self.creatingStateChanged.emit(is_creating = True)
  52. upload_backup_job = CreateBackupJob(self.BACKUP_URL)
  53. upload_backup_job.finished.connect(self._onUploadFinished)
  54. upload_backup_job.start()
  55. def _onUploadFinished(self, job: "CreateBackupJob") -> None:
  56. if job.backup_upload_error_message != "":
  57. # If the job contains an error message we pass it along so the UI can display it.
  58. self.creatingStateChanged.emit(is_creating = False, error_message = job.backup_upload_error_message)
  59. else:
  60. self.creatingStateChanged.emit(is_creating = False)
  61. def restoreBackup(self, backup: Dict[str, Any]) -> None:
  62. self.restoringStateChanged.emit(is_restoring = True)
  63. download_url = backup.get("download_url")
  64. if not download_url:
  65. # If there is no download URL, we can't restore the backup.
  66. Logger.warning("backup download_url is missing. Aborting backup.")
  67. self.restoringStateChanged.emit(is_restoring = False,
  68. error_message = catalog.i18nc("@info:backup_status",
  69. "There was an error trying to restore your backup."))
  70. return
  71. restore_backup_job = RestoreBackupJob(backup)
  72. restore_backup_job.finished.connect(self._onRestoreFinished)
  73. restore_backup_job.start()
  74. def _onRestoreFinished(self, job: "RestoreBackupJob") -> None:
  75. if job.restore_backup_error_message != "":
  76. # If the job contains an error message we pass it along so the UI can display it.
  77. self.restoringStateChanged.emit(is_restoring = False)
  78. else:
  79. self.restoringStateChanged.emit(is_restoring = False, error_message = job.restore_backup_error_message)
  80. def deleteBackup(self, backup_id: str, finished_callable: Callable[[bool], None]):
  81. def finishedCallback(reply: QNetworkReply, ca: Callable[[bool], None] = finished_callable) -> None:
  82. self._onDeleteRequestCompleted(reply, ca)
  83. def errorCallback(reply: QNetworkReply, error: QNetworkReply.NetworkError, ca: Callable[[bool], None] = finished_callable) -> None:
  84. self._onDeleteRequestCompleted(reply, ca, error)
  85. HttpRequestManager.getInstance().delete(
  86. url = "{}/{}".format(self.BACKUP_URL, backup_id),
  87. callback = finishedCallback,
  88. error_callback = errorCallback,
  89. scope= self._json_cloud_scope
  90. )
  91. @staticmethod
  92. def _onDeleteRequestCompleted(reply: QNetworkReply, callable: Callable[[bool], None], error: Optional["QNetworkReply.NetworkError"] = None) -> None:
  93. callable(HttpRequestManager.replyIndicatesSuccess(reply, error))