Skip to content
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

(backport) Fix journalist association bug in draftreplies #1188

Merged
merged 1 commit into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
def upgrade():
"""
Fix reply association with journalist by updating journalist uuid to journalist id in the
journalist_id column.
journalist_id column for the replies and draftreplies tables.
"""
conn = op.get_bind()
cursor = conn.execute("""
Expand All @@ -28,25 +28,47 @@ def upgrade():
""")

replies_with_incorrect_associations = cursor.fetchall()
if not replies_with_incorrect_associations:
return

conn.execute("""
UPDATE replies
SET journalist_id=
(
SELECT users.id
FROM users
WHERE journalist_id=users.uuid
)
WHERE exists
(
SELECT users.id
FROM users
WHERE journalist_id=users.uuid
);
if replies_with_incorrect_associations:
conn.execute("""
UPDATE replies
SET journalist_id=
(
SELECT users.id
FROM users
WHERE journalist_id=users.uuid
)
WHERE exists
(
SELECT users.id
FROM users
WHERE journalist_id=users.uuid
);
""")

cursor = conn.execute("""
SELECT journalist_id
FROM draftreplies, users
WHERE journalist_id=users.uuid;
""")

draftreplies_with_incorrect_associations = cursor.fetchall()
if draftreplies_with_incorrect_associations:
conn.execute("""
UPDATE draftreplies
SET journalist_id=
(
SELECT users.id
FROM users
WHERE journalist_id=users.uuid
)
WHERE exists
(
SELECT users.id
FROM users
WHERE journalist_id=users.uuid
);
""")


def downgrade():
"""
Expand Down
26 changes: 21 additions & 5 deletions tests/migrations/test_a4bf1f58ce69.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import subprocess

from securedrop_client import db
from securedrop_client.db import Reply, User
from securedrop_client.db import DraftReply, Reply, User

from .utils import add_reply, add_source, add_user
from .utils import add_draft_reply, add_reply, add_source, add_user

random.seed("=^..^=..^=..^=")

Expand All @@ -28,7 +28,8 @@ def __init__(self, homedir):

def load_data(self):
"""
Load data that has the bug where user.uuid is stored in replies.journalist_id.
Load data that has the bug where user.uuid is stored in replies.journalist_id and
draftreplies.journalist_id.
"""
for _ in range(self.NUM_SOURCES):
add_source(self.session)
Expand Down Expand Up @@ -57,12 +58,20 @@ def load_data(self):
source_id = random.randint(1, self.NUM_SOURCES)
add_reply(self.session, journalist.uuid, source_id)

# Add draft replies from randomly-selected journalists to a randomly-selected sources
for _ in range(1, self.NUM_REPLIES):
journalist_id = random.randint(1, self.NUM_USERS)
journalist = self.session.query(User).filter_by(id=journalist_id).one()
source_id = random.randint(1, self.NUM_SOURCES)
add_draft_reply(self.session, journalist.uuid, source_id)

self.session.commit()

def check_upgrade(self):
"""
Make sure each reply in the replies table has the correct journalist_id stored for the
associated journalist by making sure a User account exists with that journalist id.
Make sure each reply in the replies and draftreplies tables have the correct journalist_id
stored for the associated journalist by making sure a User account exists with that
journalist id.
"""
replies = self.session.query(Reply).all()
assert len(replies)
Expand All @@ -71,6 +80,13 @@ def check_upgrade(self):
# Will fail if User does not exist
self.session.query(User).filter_by(id=reply.journalist_id).one()

draftreplies = self.session.query(DraftReply).all()
assert len(draftreplies)

for draftreply in draftreplies:
# Will fail if User does not exist
self.session.query(User).filter_by(id=draftreply.journalist_id).one()

self.session.close()


Expand Down
47 changes: 46 additions & 1 deletion tests/migrations/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from sqlalchemy import text
from sqlalchemy.orm.session import Session

from securedrop_client.db import DownloadError, Source
from securedrop_client.db import DownloadError, ReplySendStatus, Source

random.seed("ᕕ( ᐛ )ᕗ")

Expand Down Expand Up @@ -289,3 +289,48 @@ def mark_reply_as_seen(session: Session, reply_id: int, journalist_id: int):
VALUES (:reply_id, :journalist_id)
"""
session.execute(text(sql), params)


def add_draft_reply(session: Session, journalist_id: int, source_id: int) -> None:
reply_send_statuses = session.query(ReplySendStatus).all()
reply_send_status_ids = [reply_send_status.id for reply_send_status in reply_send_statuses]

content = random_chars(1000)

source = session.query(Source).filter_by(id=source_id).one()

file_counter = len(source.collection) + 1

params = {
"uuid": str(uuid4()),
"journalist_id": journalist_id,
"source_id": source_id,
"file_counter": file_counter,
"content": content,
"send_status_id": random.choice(reply_send_status_ids),
"timestamp": random_datetime(),
}

sql = """
INSERT INTO draftreplies
(
uuid,
journalist_id,
source_id,
file_counter,
content,
send_status_id,
timestamp
)
VALUES
(
:uuid,
:journalist_id,
:source_id,
:file_counter,
:content,
:send_status_id,
:timestamp
)
"""
session.execute(text(sql), params)