Skip to content

Commit c267ce7

Browse files
rocodescfm
authored andcommitted
Address deletion condition when accessing orm object attributes in controller delete_sources. Ensure DeleteSourceDialog is shown even with no sources selected in widgets.py and improve comments.
Fix new FileWidget functional test.
1 parent 41b4299 commit c267ce7

File tree

3 files changed

+18
-5
lines changed

3 files changed

+18
-5
lines changed

client/securedrop_client/gui/widgets.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ def on_action_triggered(self) -> None:
553553
# The current source selection is continuously received by the controller
554554
# as the user selects and deselects; here we retrieve the selection
555555
targets = self.controller.get_selected_sources()
556-
if targets:
556+
if targets is not None:
557557
dialog = DeleteSourceDialog(targets)
558558
dialog.accepted.connect(lambda: self.controller.delete_sources(targets))
559559
dialog.exec()
@@ -904,6 +904,8 @@ def on_source_changed(self) -> None:
904904
# One source selected; prepare the conversation widget
905905
try:
906906
source = self.source_list.get_selected_source()
907+
908+
# Avoid race between user selection and remote deletion
907909
if not source:
908910
return
909911

@@ -1299,6 +1301,7 @@ def schedule_source_management(slice_size: int = slice_size) -> None:
12991301
QTimer.singleShot(1, schedule_source_management)
13001302

13011303
def get_selected_source(self) -> Optional[Source]:
1304+
# if len == 0, return None
13021305
if not self.selectedItems():
13031306
return None
13041307

client/securedrop_client/logic.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,17 @@ def delete_sources(self, sources: list[db.Source]) -> None:
10601060
the failure handler will display an error.
10611061
"""
10621062
for source in sources:
1063-
job = DeleteSourceJob(source.uuid)
1063+
try:
1064+
# Accessing source.uuid requires the source object to be
1065+
# present in the database.
1066+
# To avoid passing and referencing orm objects, a simplified
1067+
# ViewModel layer decoupled from the db that presents data to the API/GUI
1068+
# would be another possibility.
1069+
job = DeleteSourceJob(source.uuid)
1070+
except sqlalchemy.orm.exc.ObjectDeletedError:
1071+
logger.warning("DeleteSourceJob requested but source already deleted")
1072+
return
1073+
10641074
job.success_signal.connect(self.on_delete_source_success)
10651075
job.failure_signal.connect(self.on_delete_source_failure)
10661076

client/tests/functional/test_deleted_file_filewidget.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@ def check_for_sources():
4040
qtbot.wait(TIME_CLICK_ACTION)
4141

4242
def conversation_with_file_is_rendered():
43-
assert gui.main_view.view_layout.itemAt(0)
44-
conversation = gui.main_view.view_layout.itemAt(0).widget()
43+
assert gui.main_view.view_layout.currentIndex() == gui.main_view.CONVERSATION_INDEX
44+
conversation = gui.main_view.view_layout.widget(gui.main_view.view_layout.currentIndex())
4545
assert isinstance(conversation, SourceConversationWrapper)
4646
file_id = list(conversation.conversation_view.current_messages.keys())[2]
4747
file_widget = conversation.conversation_view.current_messages[file_id]
4848
assert isinstance(file_widget, FileWidget)
4949

5050
# Get the selected source conversation that contains a file attachment
5151
qtbot.waitUntil(conversation_with_file_is_rendered, timeout=TIME_RENDER_CONV_VIEW)
52-
conversation = gui.main_view.view_layout.itemAt(0).widget()
52+
conversation = gui.main_view.view_layout.widget(gui.main_view.view_layout.currentIndex())
5353
file_id = list(conversation.conversation_view.current_messages.keys())[2]
5454
file_widget = conversation.conversation_view.current_messages[file_id]
5555

0 commit comments

Comments
 (0)