def getBackups(self, changed: Callable[[List[Dict[str, Any]]], None]) -> None: def callback( reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None: if error is not None: Logger.log("w", "Could not get backups: " + str(error)) changed([]) return backup_list_response = HttpRequestManager.readJSON(reply) if "data" not in backup_list_response: Logger.log( "w", "Could not get backups from remote, actual response body was: %s", str(backup_list_response)) changed([]) # empty list of backups return changed(backup_list_response["data"]) HttpRequestManager.getInstance().get(self.BACKUP_URL, callback=callback, error_callback=callback, scope=self._json_cloud_scope)
def abortRequest(self, request_id: str) -> None: """Aborts a single request""" if request_id in self._ongoing_requests and self._ongoing_requests[ request_id]: HttpRequestManager.getInstance().abortRequest( self._ongoing_requests[request_id]) self._ongoing_requests[request_id] = None
def _onUploadSlotCompleted(self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None: if HttpRequestManager.safeHttpStatus(reply) >= 300: replyText = HttpRequestManager.readText(reply) Logger.warning("Could not request backup upload: %s", replyText) self.backup_upload_error_message = self.DEFAULT_UPLOAD_ERROR_MESSAGE if HttpRequestManager.safeHttpStatus(reply) == 400: errors = json.loads(replyText)["errors"] if "moreThanMaximum" in [error["code"] for error in errors if error["meta"] and error["meta"]["field_name"] == "backup_size"]: self.backup_upload_error_message = catalog.i18nc("@error:file_size", "The backup exceeds the maximum file size.") self._job_done.set() return if error is not None: Logger.warning("Could not request backup upload: %s", HttpRequestManager.qt_network_error_name(error)) self.backup_upload_error_message = self.DEFAULT_UPLOAD_ERROR_MESSAGE self._job_done.set() return backup_upload_url = HttpRequestManager.readJSON(reply)["data"]["upload_url"] # Upload the backup to storage. HttpRequestManager.getInstance().put( backup_upload_url, data=self._backup_zip, callback=self._uploadFinishedCallback, error_callback=self._uploadFinishedCallback )
def _onUploadSlotCompleted( self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None: if error is not None: Logger.warning(str(error)) self.backup_upload_error_message = self.DEFAULT_UPLOAD_ERROR_MESSAGE self._job_done.set() return if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) >= 300: Logger.warning("Could not request backup upload: %s", HttpRequestManager.readText(reply)) self.backup_upload_error_message = self.DEFAULT_UPLOAD_ERROR_MESSAGE self._job_done.set() return backup_upload_url = HttpRequestManager.readJSON( reply)["data"]["upload_url"] # Upload the backup to storage. HttpRequestManager.getInstance().put( backup_upload_url, data=self._backup_zip, callback=self._uploadFinishedCallback, error_callback=self._uploadFinishedCallback)
def unsunscribeUserFromPackage(self, package_id: str) -> None: """Unsubscribe the user (if logged in) from the package :param package_id: the package identification string """ if self._account.isLoggedIn: HttpRequestManager.getInstance().delete( url=f"{USER_PACKAGES_URL}/{package_id}", scope=self._scope)
def _subscribe(self, package_id: str) -> None: """You probably don't want to use this directly. All installed packages will be automatically subscribed.""" Logger.debug("Subscribing to {}", package_id) data = "{\"data\": {\"package_id\": \"%s\", \"sdk_version\": \"%s\"}}" % ( package_id, CloudApiModel.sdk_version) HttpRequestManager.getInstance().put( url=CloudApiModel.api_url_user_packages, data=data.encode(), scope=self._scope)
def run(self) -> None: url = self._backup.get("download_url") assert url is not None HttpRequestManager.getInstance().get( url = url, callback = self._onRestoreRequestCompleted, error_callback = self._onRestoreRequestCompleted ) self._job_done.wait() # A job is considered finished when the run function completes
def download(self, model: SubscribedPackagesModel) -> None: if self._started: Logger.error("Download already started. Create a new %s instead", self.__class__.__name__) return manager = HttpRequestManager.getInstance() for item in model.items: package_id = item["package_id"] def finishedCallback(reply: QNetworkReply, pid = package_id) -> None: self._onFinished(pid, reply) def progressCallback(rx: int, rt: int, pid = package_id) -> None: self._onProgress(pid, rx, rt) def errorCallback(reply: QNetworkReply, error: QNetworkReply.NetworkError, pid = package_id) -> None: self._onError(pid) request_data = manager.get( item["download_url"], callback = finishedCallback, download_progress_callback = progressCallback, error_callback = errorCallback, scope = self._scope) self._progress[package_id] = { "received": 0, "total": 1, # make sure this is not considered done yet. Also divByZero-safe "file_written": None, "request_data": request_data, "package_model": item } self._started = True self._progress_message.show()
def test_getBasicAuthSuccess() -> None: time.sleep(0.5) app = QCoreApplication([]) http_request_manager = HttpRequestManager.getInstance() cbo = mock.Mock() def callback(*args, **kwargs): cbo.callback(*args, **kwargs) # quit now so we don't need to wait http_request_manager.callLater(0, app.quit) def error_callback(*args, **kwargs): cbo.callback(*args, **kwargs) # quit now so we don't need to wait http_request_manager.callLater(0, app.quit) request_data = http_request_manager.get( url="http://localhost:8080/auth", headers_dict={"Authorization": "Basic dXNlcjp1c2Vy"}, callback=callback, error_callback=error_callback) # Make sure that if something goes wrong, we quit after 10 seconds http_request_manager.callLater(10.0, app.quit) app.exec() http_request_manager.cleanup() # Remove all unscheduled events cbo.callback.assert_called_once_with(request_data.reply) cbo.error_callback.assert_not_called()
def test_getFail404() -> None: time.sleep(0.5) app = QCoreApplication([]) http_request_manager = HttpRequestManager.getInstance() cbo = mock.Mock() def callback(*args, **kwargs): cbo.callback(*args, **kwargs) # quit now so we don't need to wait http_request_manager.callLater(0, app.quit) def error_callback(*args, **kwargs): cbo.error_callback(*args, **kwargs) # quit now so we don't need to wait http_request_manager.callLater(0, app.quit) request_data = http_request_manager.get( url="http://localhost:8080/do_not_exist", callback=callback, error_callback=error_callback) # Make sure that if something goes wrong, we quit after 10 seconds http_request_manager.callLater(10.0, app.quit) app.exec() http_request_manager.cleanup() # Remove all unscheduled events cbo.error_callback.assert_called_once_with( request_data.reply, QNetworkReply.ContentNotFoundError) cbo.callback.assert_not_called()
def __init__(self, parent: Optional["QObject"] = None) -> None: super().__init__(parent) manager = HttpRequestManager.getInstance() self._is_internet_reachable = manager.isInternetReachable # type: bool manager.internetReachableChanged.connect( self._onInternetReachableChanged)
def test_getTimeout() -> None: time.sleep(0.5) app = QCoreApplication([]) http_request_manager = HttpRequestManager.getInstance() cbo = mock.Mock() def error_callback(*args, **kwargs): cbo.error_callback(*args, **kwargs) # quit now so we don't need to wait http_request_manager.callLater(0, app.quit) request_data = http_request_manager.get( url="http://localhost:8080/timeout", error_callback=error_callback, timeout=4) # Make sure that if something goes wrong, we quit after 10 seconds http_request_manager.callLater(10.0, app.quit) app.exec() http_request_manager.cleanup() # Remove all unscheduled events cbo.error_callback.assert_called_once_with( request_data.reply, QNetworkReply.OperationCanceledError) assert request_data.is_aborted_due_to_timeout
def download(self, package_id: str, url: str, update: bool = False) -> None: """Initiate the download request :param package_id: the package identification string :param url: the URL from which the package needs to be obtained :param update: A flag if this is download request is an update process """ if url == "": url = f"{PACKAGES_URL}/{package_id}/download" def downloadFinished(reply: "QNetworkReply") -> None: self._downloadFinished(package_id, reply, update) def downloadError(reply: "QNetworkReply", error: "QNetworkReply.NetworkError") -> None: self._downloadError(package_id, update, reply, error) self._ongoing_requests[ "download_package"] = HttpRequestManager.getInstance().get( url, scope=self._scope, callback=downloadFinished, error_callback=downloadError)
def _onUploadSlotCompleted( self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None: if HttpRequestManager.safeHttpStatus(reply) >= 300: replyText = HttpRequestManager.readText(reply) Logger.warning("Could not request backup upload: %s", replyText) self.backup_upload_error_message = self.DEFAULT_UPLOAD_ERROR_MESSAGE if HttpRequestManager.safeHttpStatus(reply) == 400: errors = json.loads(replyText)["errors"] if "moreThanMaximum" in [ error["code"] for error in errors if error["meta"] and error["meta"]["field_name"] == "backup_size" ]: if self._backup_zip is None: # will never happen; keep mypy happy zip_error = "backup is None." else: zip_error = "{} exceeds max size.".format( str(len(self._backup_zip))) sentry_sdk.capture_message( "backup failed: {}".format(zip_error), level="warning") self.backup_upload_error_message = catalog.i18nc( "@error:file_size", "The backup exceeds the maximum file size.") from sentry_sdk import capture_message self._job_done.set() return if error is not None: Logger.warning("Could not request backup upload: %s", HttpRequestManager.qt_network_error_name(error)) self.backup_upload_error_message = self.DEFAULT_UPLOAD_ERROR_MESSAGE self._job_done.set() return backup_upload_url = HttpRequestManager.readJSON( reply)["data"]["upload_url"] # Upload the backup to storage. HttpRequestManager.getInstance().put( backup_upload_url, data=self._backup_zip, callback=self._uploadFinishedCallback, error_callback=self._uploadFinishedCallback)
def subscribeUserToPackage(self, package_id: str, sdk_version: str) -> None: """Subscribe the user (if logged in) to the package for a given SDK :param package_id: the package identification string :param sdk_version: the SDK version """ if self._account.isLoggedIn: HttpRequestManager.getInstance().put(url=USER_PACKAGES_URL, data=json.dumps({ "data": { "package_id": package_id, "sdk_version": sdk_version } }).encode(), scope=self._scope)
def deleteBackup(self, backup_id: str, finished_callable: Callable[[bool], None]): def finishedCallback( reply: QNetworkReply, ca: Callable[[bool], None] = finished_callable) -> None: self._onDeleteRequestCompleted(reply, ca) def errorCallback( reply: QNetworkReply, error: QNetworkReply.NetworkError, ca: Callable[[bool], None] = finished_callable) -> None: self._onDeleteRequestCompleted(reply, ca, error) HttpRequestManager.getInstance().delete(url="{}/{}".format( self.BACKUP_URL, backup_id), callback=finishedCallback, error_callback=errorCallback, scope=self._json_cloud_scope)
def _requestUploadSlot(self, backup_metadata: Dict[str, Any], backup_size: int) -> None: """Request a backup upload slot from the API. :param backup_metadata: A dict containing some meta data about the backup. :param backup_size: The size of the backup file in bytes. """ payload = json.dumps({"data": {"backup_size": backup_size, "metadata": backup_metadata } }).encode() HttpRequestManager.getInstance().put( self._api_backup_url, data = payload, callback = self._onUploadSlotCompleted, error_callback = self._onUploadSlotCompleted, scope = self._json_cloud_scope)
def __init__(self, app: CuraApplication, on_error: Callable[[List[CloudError]], None]) -> None: super().__init__() self._app = app self._account = app.getCuraAPI().account self._scope = JsonDecoratorScope(UltimakerCloudScope(app)) self._http = HttpRequestManager.getInstance() self._on_error = on_error self._upload = None # type: Optional[ToolPathUploader]
def _openSelectedFile(self, temp_dir: str, project_name: str, file_name: str, download_url: str) -> None: """ Downloads, then opens, the single specified file. :param temp_dir: The already created temporary directory where the files will be stored. :param project_name: Name of the project the file belongs to (used for error reporting). :param file_name: Name of the file to be downloaded and opened (used for error reporting). :param download_url: This url will be downloaded, then the downloaded file will be opened in Cura. """ if not download_url: Logger.log("e", "No download url for file '{}'".format(file_name)) return progress_message = Message(text = "{0}/{1}".format(project_name, file_name), dismissable = False, lifetime = 0, progress = 0, title = "Downloading...") progress_message.setProgress(0) progress_message.show() def progressCallback(rx: int, rt: int) -> None: progress_message.setProgress(math.floor(rx * 100.0 / rt)) def finishedCallback(reply: QNetworkReply) -> None: progress_message.hide() try: with open(os.path.join(temp_dir, file_name), "wb+") as temp_file: bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE) while bytes_read: temp_file.write(bytes_read) bytes_read = reply.read(self.DISK_WRITE_BUFFER_SIZE) CuraApplication.getInstance().processEvents() temp_file_name = temp_file.name except IOError as ex: Logger.logException("e", "Can't write Digital Library file {0}/{1} download to temp-directory {2}.", ex, project_name, file_name, temp_dir) Message( text = "Failed to write to temporary file for '{}'.".format(file_name), title = "File-system error", lifetime = 10 ).show() return CuraApplication.getInstance().readLocalFile( QUrl.fromLocalFile(temp_file_name), add_to_recent_files = False) def errorCallback(reply: QNetworkReply, error: QNetworkReply.NetworkError, p = project_name, f = file_name) -> None: progress_message.hide() Logger.error("An error {0} {1} occurred while downloading {2}/{3}".format(str(error), str(reply), p, f)) Message( text = "Failed Digital Library download for '{}'.".format(f), title = "Network error {}".format(error), lifetime = 10 ).show() download_manager = HttpRequestManager.getInstance() download_manager.get(download_url, callback = finishedCallback, download_progress_callback = progressCallback, error_callback = errorCallback, scope = UltimakerCloudScope(CuraApplication.getInstance()))
def checkForUpdates(self, packages: List[Dict[str, Any]]) -> None: installed_packages = "&".join([ f"installed_packages={package['package_id']}:{package['package_version']}" for package in packages ]) request_url = f"{PACKAGE_UPDATES_URL}?{installed_packages}" self._ongoing_requests[ "check_updates"] = HttpRequestManager.getInstance().get( request_url, scope=self._scope, callback=self._parseResponse)
def updatePackages(self) -> None: """ Make a request for the first paginated page of packages. When the request is done, the list will get updated with the new package models. """ self.setErrorMessage("") # Clear any previous errors. self.setIsLoading(True) self._ongoing_requests["get_packages"] = HttpRequestManager.getInstance().get( self._request_url, scope = self._scope, callback = self._parseResponse, error_callback = self._onError )
def __init__(self, app: CuraApplication, on_error: Callable[[List[CloudError]], None]) -> None: """Initializes a new cloud API client. :param app: :param account: The user's account object :param on_error: The callback to be called whenever we receive errors from the server. """ super().__init__() self._app = app self._account = app.getCuraAPI().account self._scope = JsonDecoratorScope(UltimakerCloudScope(app)) self._http = HttpRequestManager.getInstance() self._on_error = on_error self._upload = None # type: Optional[ToolPathUploader]
def __init__(self, parent: Optional["QObject"] = None): super().__init__(parent) self._http = HttpRequestManager.getInstance() self._statuses = { self.ULTIMAKER_CLOUD_STATUS_URL: True, "http://example.com": True } # Create a timer for automatic updates self._update_timer = QTimer() self._update_timer.setInterval(int(self.UPDATE_INTERVAL * 1000)) # The timer is restarted automatically self._update_timer.setSingleShot(False) self._update_timer.timeout.connect(self._update) self._update_timer.start()
def checkNewVersion(self, silent=False, display_same_version=True) -> None: """Connect with software.ultimaker.com, load latest.json and check version info. If the version info is higher then the current version, spawn a message to allow the user to download it. :param silent: Suppresses messages other than "new version found" messages. This is used when checking for a new version at startup. :param display_same_version: Whether to display the same update message twice (True) or suppress the update message if the user has already seen the update for a particular version. When manually checking for updates, the user wants to display the update even if he's already seen it. """ http_manager = HttpRequestManager.getInstance() Logger.log("i", "Checking for new version") http_manager.get(self.url, callback=lambda reply: self._onRequestCompleted( reply, silent, display_same_version)) self._download_url = None
def test_getSuccessful() -> None: app = QCoreApplication([]) http_request_manager = HttpRequestManager.getInstance() cbo = mock.Mock() def callback(*args, **kwargs): cbo.callback(*args, **kwargs) # quit now so we don't need to wait http_request_manager.callLater(0, app.quit) request_data = http_request_manager.get(url = "http://localhost:8080/success", callback = callback) # Make sure that if something goes wrong, we quit after 10 seconds http_request_manager.callLater(10.0, app.quit) app.exec() http_request_manager.cleanup() # Remove all unscheduled events cbo.callback.assert_called_once_with(request_data.reply)
def __init__(self, application: CuraApplication, on_error: Callable[[List[CloudError]], None], projects_limit_per_page: Optional[int] = None) -> None: """Initializes a new digital factory API client. :param application: :param on_error: The callback to be called whenever we receive errors from the server. """ super().__init__() self._application = application self._account = application.getCuraAPI().account self._scope = JsonDecoratorScope(UltimakerCloudScope(application)) self._http = HttpRequestManager.getInstance() self._on_error = on_error self._file_uploader = None # type: Optional[DFFileUploader] self._projects_pagination_mgr = PaginationManager( limit=projects_limit_per_page ) if projects_limit_per_page else None # type: Optional[PaginationManager]
def onUploadCompleted(self, reply: "QNetworkReply") -> None: """ When we've successfully uploaded the archive to the cloud, we need to notify the API to start syncing that archive to every printer. :param reply: The reply from the cloud storage when the upload succeeded. """ for container_stack in self._printer_metadata: cluster_id = container_stack["um_cloud_cluster_id"] printer_id = container_stack["host_guid"] http = HttpRequestManager.getInstance() http.post( url=self.UPLOAD_CONFIRM_URL.format( cluster_id=cluster_id, cluster_printer_id=printer_id), callback=functools.partial(self.onUploadConfirmed, printer_id), error_callback=functools.partial( self.onUploadConfirmed, printer_id ), # Let this same function handle the error too. scope=self._scope, data=json.dumps({ "data": { "material_profile_id": self._archive_remote_id } }).encode("UTF-8"))
def unsubscribe(self, package_id: str) -> None: url = CloudApiModel.userPackageUrl(package_id) HttpRequestManager.getInstance().delete(url=url, scope=self._scope)
def abort(self) -> None: manager = HttpRequestManager.getInstance() for item in self._progress.values(): manager.abortRequest(item["request_data"])
def getHttpRequestManager(self) -> "HttpRequestManager": if not self._http_network_request_manager: self._http_network_request_manager = HttpRequestManager.getInstance(parent = self) return self._http_network_request_manager