-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
no longer stop metadata syncs unless logged out
- Loading branch information
Allie Crevier
committed
Jan 28, 2020
1 parent
8fe8158
commit c7522fd
Showing
5 changed files
with
153 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from typing import Any | ||
import logging | ||
|
||
from sdclientapi import API | ||
from sqlalchemy.orm.session import Session | ||
|
||
from securedrop_client.api_jobs.base import ApiJob | ||
from securedrop_client.crypto import GpgHelper, CryptoError | ||
from securedrop_client.storage import get_remote_data, update_local_storage | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class MetadataSyncJob(ApiJob): | ||
''' | ||
Update source metadata such that new download jobs can be added to the queue. | ||
''' | ||
|
||
def __init__(self, data_dir: str, gpg: GpgHelper) -> None: | ||
super().__init__(remaining_attempts=15) | ||
self.data_dir = data_dir | ||
self.gpg = gpg | ||
|
||
def call_api(self, api_client: API, session: Session) -> Any: | ||
''' | ||
Override ApiJob. | ||
Download new metadata, update the local database, import new keys, and | ||
then the success signal will let the controller know to add any new download | ||
jobs. | ||
''' | ||
|
||
# TODO: Once https://github.com/freedomofpress/securedrop-client/issues/648, we will want to | ||
# pass the default request timeout to api calls instead of setting it on the api object | ||
# directly. | ||
api_client.default_request_timeout = 20 | ||
remote_sources, remote_submissions, remote_replies = get_remote_data(api_client) | ||
update_local_storage(session, | ||
remote_sources, | ||
remote_submissions, | ||
remote_replies, | ||
self.data_dir) | ||
|
||
for source in remote_sources: | ||
if source.key and source.key.get('type', None) == 'PGP': | ||
pub_key = source.key.get('public', None) | ||
fingerprint = source.key.get('fingerprint', None) | ||
if not pub_key or not fingerprint: | ||
# The below line needs to be excluded from the coverage computation | ||
# as it will show as uncovered due to a cpython compiler optimziation. | ||
# See: https://bugs.python.org/issue2506 | ||
continue # pragma: no cover | ||
try: | ||
self.gpg.import_key(source.uuid, pub_key, fingerprint) | ||
except CryptoError: | ||
logger.warning('Failed to import key for source {}'.format(source.uuid)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import logging | ||
|
||
from PyQt5.QtCore import pyqtSignal, QObject, QThread, QTimer, Qt | ||
from sqlalchemy.orm import scoped_session | ||
from sdclientapi import API, RequestTimeoutError | ||
|
||
from securedrop_client.api_jobs.sync import MetadataSyncJob | ||
from securedrop_client.crypto import GpgHelper | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class ApiSync(QObject): | ||
''' | ||
ApiSync continuously executes a MetadataSyncJob, waiting 15 seconds between jobs. | ||
''' | ||
sync_started = pyqtSignal() | ||
sync_success = pyqtSignal() | ||
sync_failure = pyqtSignal(Exception) | ||
|
||
TIME_BETWEEN_SYNCS_MS = 1000 * 15 # fifteen seconds between syncs | ||
|
||
def __init__( | ||
self, api_client: API, session_maker: scoped_session, gpg: GpgHelper, data_dir: str | ||
): | ||
super().__init__() | ||
|
||
self.api_client = api_client | ||
self.session_maker = session_maker | ||
self.gpg = gpg | ||
self.data_dir = data_dir | ||
|
||
self.sync_thread = QThread() | ||
self.sync_thread.started.connect(self._sync) | ||
|
||
def start(self, api_client: API) -> None: | ||
''' | ||
Stop metadata syncs. | ||
''' | ||
self.api_client = api_client | ||
|
||
if not self.sync_thread.isRunning(): | ||
logger.debug('Starting sync thread') | ||
self.sync_thread.start() | ||
|
||
def stop(self): | ||
''' | ||
Stop metadata syncs. | ||
''' | ||
self.api_client = None | ||
|
||
if self.sync_thread.isRunning(): | ||
logger.debug('Stopping sync thread') | ||
self.sync_thread.quit() | ||
|
||
def sync(self): | ||
''' | ||
Create and run a new MetadataSyncJob. | ||
''' | ||
job = MetadataSyncJob(self.data_dir, self.gpg) | ||
job.success_signal.connect(self.on_sync_success, type=Qt.QueuedConnection) | ||
job.failure_signal.connect(self.on_sync_failure, type=Qt.QueuedConnection) | ||
|
||
session = self.session_maker() | ||
job._do_call_api(self.api_client, session) | ||
self.sync_started.emit() | ||
|
||
def on_sync_success(self) -> None: | ||
self.sync_success.emit() | ||
QTimer.singleShot(self.TIME_BETWEEN_SYNCS_MS, self.sync) | ||
|
||
def on_sync_failure(self, result: Exception) -> None: | ||
self.sync_failure.emit(result) | ||
if isinstance(result, RequestTimeoutError): | ||
QTimer.singleShot(self.TIME_BETWEEN_SYNCS_MS, self.sync) |