-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
merge [Draft]Reply
models
#1634
Comments
I'll recap for the record my conversation with @gonzalo-bulnes about the integration strategy here. There are three layers of the Client stack that will be aware of this state machine: broadly speaking, the database (persistence), the controller and processing jobs (logic), and the GUI (presentation). Qt has its own I was concerned that we would have to replicate this state machine between the model-level FSM and a widget-level We agreed that, while there's room to rethink the relation between the Client's domain model (in |
I spent some time earlier this month (a) long-listing Python FSM libraries, (b) short-listing the top three by PyPI downloads and dependents, and (c) proving integration of each of those three. The largest diffstat (again, just for the integration, not including a complete state machine) is 110 lines. My key finding is that, all else being equal, we should prefer an FSM library that provides a singleton machine for an arbitrary number of models. (Yes, this terminology becomes terribly confusing in the context of an ORM, which we also have.) By analogy to OOP: We want our instances (models) to reuse the functionality (transitions) provided by the class (machine), not copy it into each and every instance. In our case, we want one Based on this testing, I recommend we use That's how I'll proceed with implementation, and I'll invite review of |
As of 2aa88fc, here's an example of how this approach concretely lets us sanity-check model state and application behavior:>>> from securedrop_client.db import Reply
>>> a = Reply(filename="0-will-succeed")
>>> a.state
'Pending'
>>> a.send_queued()
True
>>> a.state
'SendPending'
>>> a.sending()
True
>>> a.state
'Sending'
>>> a.sent()
True
>>> a.state
'Ready'
>>> a.download_queued()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/user/securedrop-client/.venv/lib/python3.9/site-packages/transitions/core.py", line 402, in trigger
return self.machine._process(func)
File "/home/user/securedrop-client/.venv/lib/python3.9/site-packages/transitions/core.py", line 1211, in _process
return trigger()
File "/home/user/securedrop-client/.venv/lib/python3.9/site-packages/transitions/core.py", line 415, in _trigger
if self._is_valid_source(event_data.state):
File "/home/user/securedrop-client/.venv/lib/python3.9/site-packages/transitions/core.py", line 452, in _is_valid_source
raise MachineError(msg)
transitions.core.MachineError: "Can't trigger event download_queued from state Ready!"
>>> b = Reply(filename="1-will-fail")
>>> b.state
'Pending'
>>> b.send_queued()
True
>>> b.state
'SendPending'
>>> b.sending()
True
>>> b.state
'Sending'
>>> b.send_failed()
True
>>> b.state
'SendFailed'
>>> b.download_queued() # recovery
True
>>> b.state
'DownloadPending'
>>> b.downloading()
True
>>> b.state
'Downloading'
>>> b.downloaded()
True
>>> b.state
'Downloaded'
>>> b.decrypted()
True
>>> b.state
'Ready' |
Abandoned per #1632 (comment). |
This ticket tracks the implementation of the refactoring proposed in freedomofpress/securedrop-engineering#8 and formally modeled in #1632, specifically:
DraftReply
SQLAlchemy model into theReply
SQLAlchemy model, including:securedrop_client.state
and/orQStateMachine
patterns.DraftReply
intoReply
objects.securedrop_client.storage
functions.securedrop_client.api_job.{download,upload}_jobs
.securedrop_client.logic.Controller
methods, signals, and slotssecuredrop_client.gui.widgets.{SpeechBubble,ReplyWidget}
methods, signals, and slots.The text was updated successfully, but these errors were encountered: