diff --git a/src/vorta/application.py b/src/vorta/application.py index 8857a51eb..8c89735b5 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -312,8 +312,8 @@ def check_failed_response(self, result: Dict[str, Any]): # extract data from the params for the borg job repo_url = result['params']['repo_url'] returncode = result['returncode'] - errors: List[Tuple[int, str]] = result['errors'] - error_message = errors[0][1] if errors else '' + errors: List[Tuple[int, dict]] = result['errors'] + error_message = errors[0]['message'] if errors else '' # Switch over returncodes if returncode == 0: diff --git a/src/vorta/borg/borg_job.py b/src/vorta/borg/borg_job.py index 9cf6a1551..5db70bf20 100644 --- a/src/vorta/borg/borg_job.py +++ b/src/vorta/borg/borg_job.py @@ -264,7 +264,7 @@ def read_async(fd): if stderr: for line in stderr.split('\n'): try: - parsed = json.loads(line) + parsed: dict = json.loads(line) if parsed['type'] == 'log_message': context = { @@ -281,7 +281,8 @@ def read_async(fd): if level_int >= logging.WARNING: # Append log to list of error messages - error_messages.append((level_int, parsed["message"])) + parsed['level'] = level_int + error_messages.append(parsed) elif parsed['type'] == 'file_status': self.app.backup_log_event.emit( diff --git a/src/vorta/views/repo_add_dialog.py b/src/vorta/views/repo_add_dialog.py index cd265a333..ab2805e88 100644 --- a/src/vorta/views/repo_add_dialog.py +++ b/src/vorta/views/repo_add_dialog.py @@ -7,6 +7,7 @@ QDialogButtonBox, QFormLayout, QLabel, + QMessageBox, QSizePolicy, ) @@ -124,11 +125,28 @@ def values(self): repo_name=self.repoName.text(), password=self.passwordInput.get_password(), extra_borg_arguments=self.extraBorgArgumentsLineEdit.text(), + is_remote_repo=self.is_remote_repo, ) return out + @classmethod + def from_values(cls, values: dict): + """Instantiate window from a values dict as returned by values property.""" + window = cls() + window.sshComboBox.setCurrentIndex(window.sshComboBox.findData(values['ssh_key'])) + window.repoURL.setText(values['repo_url']) + window.repoName.setText(values['repo_name']) + window.passwordInput.passwordLineEdit.setText(values['password']) + # do not set confirmLineEdit, let user confirm password + window._set_status("Autofilled password from previous window.") + window.extraBorgArgumentsLineEdit.setText(values['extra_borg_arguments']) + window.is_remote_repo = values['is_remote_repo'] + return window + class AddRepoWindow(RepoWindow): + """Dialog for initializing a new repository and adding it.""" + def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("Add New Repository") @@ -163,6 +181,14 @@ def values(self): out['encryption'] = self.encryptionComboBox.currentData() return out + @classmethod + def from_values(cls, values: dict): + """Instantiate window from a values dict as returned by values property.""" + window = super().from_values(values) + if 'encryption' in values: + window.encryptionComboBox.setCurrentIndex(window.encryptionComboBox.findData(values['encryption'])) + return window + def init_encryption(self): if borg_compat.check('V2'): encryption_algos = [ @@ -224,6 +250,8 @@ def run(self): class ExistingRepoWindow(RepoWindow): + """Dialog for adding an existing repository.""" + def __init__(self): super().__init__() self.title.setText(self.tr('Connect to existing Repository')) @@ -234,12 +262,45 @@ def __init__(self): self.repoDataFormLayout.addRow(self.passwordLabel, self.passwordInput) def set_password(self, URL): - '''Autofill password from keyring only if current entry is empty''' + """Autofill password from keyring only if current entry is empty""" password = VortaKeyring.get_keyring().get_password('vorta-repo', URL) if password and self.passwordInput.get_password() == "": self._set_status(self.tr("Autofilled password from password manager.")) self.passwordInput.setText(password) + def run_result(self, result): + """Handle result of BorgInfoRepoJob.""" + self.saveButton.setEnabled(True) + if result['returncode'] == 0: + self.added_repo.emit(result) + self.accept() + else: + self._set_status(self.tr('Unable to add your repository.')) + for error in result['errors']: + if 'msgid' in error and error['msgid'] in [ + 'Repository.DoesNotExist', + 'Repository.InvalidRepository', + 'InvalidRepository', + ]: + self.init_new_repo() + break + + def init_new_repo(self): + """Ask user if they want to initialize a new repository instead.""" + answer = QMessageBox.question( + self, + 'Add new Repository instead', + self.tr("This repository doesn't seem to exist. Do you want to initialize a new repository?"), + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + ) + if answer == QMessageBox.StandardButton.Yes: + init_dialog = AddRepoWindow.from_values(self.values) + repo_tab = self.parent() + init_dialog.setParent(repo_tab, QtCore.Qt.WindowType.Sheet) + init_dialog.added_repo.connect(repo_tab.process_new_repo) + init_dialog.open() + self.close() + def run(self): if self.validate(): params = BorgInfoRepoJob.prepare(self.values) diff --git a/tests/unit/test_repo.py b/tests/unit/test_repo.py index e072119d1..a540df293 100644 --- a/tests/unit/test_repo.py +++ b/tests/unit/test_repo.py @@ -289,7 +289,7 @@ def test_repo_check_failed_response(qapp, qtbot, mocker, response): mock_result: Dict[str, Any] = { 'params': {'repo_url': 'test_repo_url'}, 'returncode': response["return_code"], - 'errors': [(0, 'test_error_message')] if response["return_code"] not in [0, 130] else None, + 'errors': [{'level': 0, 'message': 'test_error_message'}] if response["return_code"] not in [0, 130] else None, } mock_exec = mocker.patch.object(QMessageBox, "exec")