Skip to content

Commit

Permalink
OpenConceptLab/ocl_issues#625 | added logic for is_exporting | source…
Browse files Browse the repository at this point in the history
…/collection version export apis to return 208 if its already exporting
  • Loading branch information
snyaggarwal committed Mar 7, 2021
1 parent 7337228 commit b4417f1
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 6 deletions.
5 changes: 4 additions & 1 deletion core/common/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ def get(self, request, *args, **kwargs): # pylint: disable=unused-argument
response['Last-Updated-Timezone'] = settings.TIME_ZONE_PLACE
return response

if version.is_processing:
if version.is_exporting:
return Response(status=status.HTTP_208_ALREADY_REPORTED)

return Response(status=status.HTTP_204_NO_CONTENT)
Expand All @@ -537,6 +537,9 @@ def post(self, request, *args, **kwargs): # pylint: disable=unused-argument

logger.debug('%s Export requested for version %s (post)', self.entity, version.version)

if version.is_exporting:
return Response(status=status.HTTP_208_ALREADY_REPORTED)

if not version.has_export():
status_code = self.handle_export_version()
return Response(status=status_code)
Expand Down
13 changes: 13 additions & 0 deletions core/common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,19 @@ def clear_processing(self):
self._background_process_ids = list()
self.save(update_fields=['_background_process_ids'])

@property
def is_exporting(self):
is_processing = self.is_processing

if is_processing:
for process_id in self._background_process_ids:
res = AsyncResult(process_id)
task_name = res.name
if task_name and task_name.startswith('core.common.tasks.export_'):
return True

return False

@property
def export_path(self):
last_update = self.last_child_update.strftime('%Y%m%d%H%M%S')
Expand Down
9 changes: 4 additions & 5 deletions core/integration_tests/tests_sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from celery_once import AlreadyQueued
from django.db import transaction
from mock import patch, Mock, ANY
from mock import patch, Mock, ANY, PropertyMock
from rest_framework.exceptions import ErrorDetail

from core.collections.tests.factories import OrganizationCollectionFactory
Expand Down Expand Up @@ -597,12 +597,11 @@ def test_get_200(self, s3_exists_mock, s3_url_for_mock):
s3_exists_mock.assert_called_once_with("username/source1_v1.{}.zip".format(self.v1_updated_at))
s3_url_for_mock.assert_called_once_with("username/source1_v1.{}.zip".format(self.v1_updated_at))

@patch('core.sources.models.Source.is_exporting', new_callable=PropertyMock)
@patch('core.common.services.S3.exists')
def test_get_208(self, s3_exists_mock):
def test_get_208(self, s3_exists_mock, is_exporting_mock):
s3_exists_mock.return_value = False
self.source_v1._background_process_ids = ['some-task-id'] # pylint: disable=protected-access
self.source_v1.save()
self.assertTrue(self.source_v1.is_processing)
is_exporting_mock.return_value = True

response = self.client.get(
'/sources/source1/v1/export/',
Expand Down
1 change: 1 addition & 0 deletions core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@
'core.common.tasks.rebuild_indexes': {'queue': 'indexing'}
}
CELERY_RESULT_BACKEND = 'redis://%s:%s/%s' % (REDIS_HOST, REDIS_PORT, REDIS_DB)
CELERY_RESULT_EXTENDED = True
CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS = {
'retry_policy': {
'timeout': 10.0
Expand Down
46 changes: 46 additions & 0 deletions core/sources/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,52 @@ def test_is_processing(self, async_result_klass_mock):
self.assertTrue(source.is_processing)
self.assertEqual(source._background_process_ids, [1, 2, 3]) # pylint: disable=protected-access

@patch('core.common.models.AsyncResult')
def test_is_exporting(self, async_result_klass_mock):
source = OrganizationSourceFactory()
self.assertFalse(source.is_exporting)

async_result_instance_mock = Mock(successful=Mock(return_value=True))
async_result_klass_mock.return_value = async_result_instance_mock

source._background_process_ids = [None, ''] # pylint: disable=protected-access
source.save()

self.assertFalse(source.is_exporting)

source._background_process_ids = ['1', '2', '3'] # pylint: disable=protected-access
source.save()

self.assertFalse(source.is_exporting)

async_result_instance_mock = Mock(successful=Mock(return_value=False), failed=Mock(return_value=True))
async_result_klass_mock.return_value = async_result_instance_mock

source._background_process_ids = [1, 2, 3] # pylint: disable=protected-access
source.save()

self.assertFalse(source.is_exporting)

async_result_instance_mock = Mock(successful=Mock(return_value=False), failed=Mock(return_value=False))
async_result_instance_mock.name = 'core.common.tasks.foobar'
async_result_klass_mock.return_value = async_result_instance_mock

source._background_process_ids = [1, 2, 3] # pylint: disable=protected-access
source.save()

self.assertFalse(source.is_exporting)

async_result_instance_mock = Mock(
name='core.common.tasks.export_source', successful=Mock(return_value=False), failed=Mock(return_value=False)
)
async_result_instance_mock.name = 'core.common.tasks.export_source'
async_result_klass_mock.return_value = async_result_instance_mock

source._background_process_ids = [1, 2, 3] # pylint: disable=protected-access
source.save()

self.assertTrue(source.is_exporting)

def test_add_processing(self):
source = OrganizationSourceFactory()
self.assertEqual(source._background_process_ids, []) # pylint: disable=protected-access
Expand Down

0 comments on commit b4417f1

Please sign in to comment.