Skip to content

Commit

Permalink
app: create separate object for draft/pending/failed replies
Browse files Browse the repository at this point in the history
  • Loading branch information
redshiftzero committed Oct 24, 2019
1 parent 950b9c3 commit e55e45f
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 189 deletions.
67 changes: 67 additions & 0 deletions alembic/versions/86b01b6290da_add_reply_draft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""add reply draft
Revision ID: 86b01b6290da
Revises: 36a79ffcfbfb
Create Date: 2019-10-17 09:45:07.643076
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '86b01b6290da'
down_revision = '36a79ffcfbfb'
branch_labels = None
depends_on = None


def upgrade():
op.create_table('replysendstatuses',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=36), nullable=False),
sa.PrimaryKeyConstraint('id', name=op.f('pk_replysendstatuses')),
sa.UniqueConstraint('name', name=op.f('uq_replysendstatuses_name'))
)

# Set the initial in-progress send statuses: PENDING, FAILED
conn = op.get_bind()
conn.execute('''
INSERT INTO replysendstatuses
('name')
VALUES
('PENDING'),
('FAILED');
''')

op.create_table(
'draftreplies',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('timestamp', sa.DateTime(), nullable=False),
sa.Column('source_id', sa.Integer(), nullable=False),
sa.Column('journalist_id', sa.Integer(), nullable=True),
sa.Column('file_counter', sa.Integer(), nullable=False),
sa.Column('content', sa.Text(), nullable=True),
sa.Column('send_status_id', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id', name=op.f('pk_draftreplies')),
sa.UniqueConstraint('uuid', name=op.f('uq_draftreplies_uuid')),
sa.ForeignKeyConstraint(
['source_id'],
['sources.id'],
name=op.f('fk_draftreplies_source_id_sources')),
sa.ForeignKeyConstraint(
['journalist_id'],
['users.id'],
name=op.f('fk_draftreplies_journalist_id_users')),
sa.ForeignKeyConstraint(
['send_status_id'],
['replysendstatuses.id'],
op.f('fk_draftreplies_send_status_id_replysendstatuses'),
)
)


def downgrade():
op.drop_table('draftreplies')
op.drop_table('replysendstatuses')
130 changes: 0 additions & 130 deletions alembic/versions/86b01b6290da_add_reply_send_status.py

This file was deleted.

12 changes: 9 additions & 3 deletions create_dev_data.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#!/usr/bin/env python3
import json
import os
from sqlalchemy.orm.exc import NoResultFound
import sys

from securedrop_client.config import Config
from securedrop_client.db import Base, make_session_maker, ReplySendStatus
from securedrop_client.api_jobs.uploads import ReplySendStatusCodes
Expand All @@ -16,6 +18,10 @@
}))

for reply_send_status in ReplySendStatusCodes:
reply_status = ReplySendStatus(reply_send_status.value)
session.add(reply_status)
session.commit()
try:
reply_status = session.query(ReplySendStatus).filter_by(
name=reply_send_status.value).one()
except NoResultFound:
reply_status = ReplySendStatus(reply_send_status.value)
session.add(reply_status)
session.commit()
8 changes: 2 additions & 6 deletions securedrop_client/api_jobs/downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@
from sqlalchemy.orm.session import Session

from securedrop_client.api_jobs.base import ApiJob
from securedrop_client.api_jobs.uploads import ReplySendStatusCodes
from securedrop_client.crypto import GpgHelper, CryptoError
from securedrop_client.db import File, Message, Reply, ReplySendStatus
from securedrop_client.db import File, Message, Reply
from securedrop_client.storage import mark_as_decrypted, mark_as_downloaded, \
set_message_or_reply_content

Expand Down Expand Up @@ -264,14 +263,11 @@ def call_decrypt(self, filepath: str, session: Session = None) -> str:
'''
with NamedTemporaryFile('w+') as plaintext_file:
self.gpg.decrypt_submission_or_reply(filepath, plaintext_file.name, is_doc=False)
success_status = session.query(ReplySendStatus).filter_by(
name=ReplySendStatusCodes.SUCCEEDED.value).one()
set_message_or_reply_content(
model_type=Message,
uuid=self.uuid,
session=session,
content=plaintext_file.read(),
send_status_id=success_status.id)
content=plaintext_file.read())
return ""


Expand Down
42 changes: 28 additions & 14 deletions securedrop_client/api_jobs/uploads.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

from securedrop_client.api_jobs.base import ApiJob
from securedrop_client.crypto import GpgHelper
from securedrop_client.db import Reply, ReplySendStatus
from securedrop_client.db import DraftReply, Reply, ReplySendStatus, Source

logger = logging.getLogger(__name__)


class ReplySendStatusCodes(Enum):
"""In progress (sending) replies can currently have the following statuses"""
PENDING = 'PENDING'
SUCCEEDED = 'SUCCEEDED'
FAILED = 'FAILED'


Expand All @@ -35,41 +35,55 @@ def call_api(self, api_client: API, session: Session) -> str:
we can return the reply uuid.
'''
try:
draft_reply_db_object = session.query(DraftReply).filter_by(uuid=self.reply_uuid).one()

encrypted_reply = self.gpg.encrypt_to_source(self.source_uuid, self.message)
reply_db_object = session.query(Reply).filter_by(uuid=self.reply_uuid).one()
source = session.query(Source).filter_by(uuid=self.source_uuid).one()
source.interaction_count += 1
filename = '{}-{}-reply.gpg'.format(source.interaction_count,
source.journalist_designation)
reply_db_object = Reply(
uuid=self.reply_uuid,
source_id=source.id,
filename=filename,
journalist_id=api_client.token_journalist_uuid,
content=self.message,
is_downloaded=True,
is_decrypted=True,
)
sdk_reply = self._make_call(encrypted_reply, api_client)

# Update reply send status to SUCCEEDED
reply_status = session.query(ReplySendStatus).filter_by(
name=ReplySendStatusCodes.SUCCEEDED.value).one()
reply_db_object.send_status_id = reply_status.id
# Update filename in case it changed on the server
# Update filename and file_counter in case they changed on the server
reply_db_object.file_counter = int(sdk_reply.filename.split('-')[0])
reply_db_object.filename = sdk_reply.filename

# Delete draft, add reply to replies table.
session.add(reply_db_object)
session.delete(draft_reply_db_object)
session.commit()

return reply_db_object.uuid
except RequestTimeoutError as e:
message = "Failed to send reply for source {id} due to Exception: {error}".format(
id=self.source_uuid, error=e)

# Update reply send status to FAILED
# Update draft reply send status to FAILED
reply_status = session.query(ReplySendStatus).filter_by(
name=ReplySendStatusCodes.FAILED.value).one()
reply_db_object.send_status_id = reply_status.id
session.add(reply_db_object)
draft_reply_db_object.send_status_id = reply_status.id
session.add(draft_reply_db_object)
session.commit()

raise SendReplyJobTimeoutError(message, self.reply_uuid)
except Exception as e:
message = "Failed to send reply for source {id} due to Exception: {error}".format(
id=self.source_uuid, error=e)

# Update reply send status to FAILED
# Update draft reply send status to FAILED
reply_status = session.query(ReplySendStatus).filter_by(
name=ReplySendStatusCodes.FAILED.value).one()
reply_db_object.send_status_id = reply_status.id
session.add(reply_db_object)
draft_reply_db_object.send_status_id = reply_status.id
session.add(draft_reply_db_object)
session.commit()

raise SendReplyJobError(message, self.reply_uuid)
Expand Down
Loading

0 comments on commit e55e45f

Please sign in to comment.