WL-1687 | Added journals access in xblock rendering
This commit is contained in:
@@ -121,7 +121,7 @@ class JournalsApiClient(object):
|
||||
return User.objects.get(username=JOURNAL_WORKER_USERNAME)
|
||||
|
||||
|
||||
def fetch_journal_access(site, user): # pylint: disable=unused-argument
|
||||
def fetch_journal_access(site, user, block_id=None): # pylint: disable=unused-argument
|
||||
"""
|
||||
Retrieve journal access record for given user.
|
||||
Retrieve if from the cache if present, otherwise send GET request to the journal access api
|
||||
@@ -143,10 +143,13 @@ def fetch_journal_access(site, user): # pylint: disable=unused-argument
|
||||
# TODO: WL-1560:
|
||||
# LMS should cache responses from Journal Access API
|
||||
# Need strategy for updating cache when new purchase happens
|
||||
journal_access_records = JournalsApiClient().client.journalaccess.get(
|
||||
user=user,
|
||||
get_latest=True
|
||||
)
|
||||
endpoint_params = {
|
||||
"user": user,
|
||||
"get_latest": True,
|
||||
}
|
||||
if block_id:
|
||||
endpoint_params['block_id'] = block_id
|
||||
journal_access_records = JournalsApiClient().client.journalaccess.get(**endpoint_params)
|
||||
return journal_access_records.get('results', [])
|
||||
except ValueError:
|
||||
return []
|
||||
|
||||
85
openedx/features/journals/tests/test_journal_xblock.py
Normal file
85
openedx/features/journals/tests/test_journal_xblock.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Test cases for journal page views.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
import mock
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse
|
||||
|
||||
from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
|
||||
from openedx.features.journals.tests.utils import (
|
||||
get_mocked_journal_access,
|
||||
override_switch
|
||||
)
|
||||
from openedx.features.journals.api import JOURNAL_INTEGRATION
|
||||
|
||||
|
||||
@mock.patch.dict(settings.FEATURES, {"JOURNALS_ENABLED": True})
|
||||
class RenderXblockByJournalAccessViewTest(LoginEnrollmentTestCase, CacheIsolationTestCase, SiteMixin):
|
||||
""" Tests for views responsible for rendering xblock in journals """
|
||||
|
||||
def setUp(self):
|
||||
super(RenderXblockByJournalAccessViewTest, self).setUp()
|
||||
self.setup_user()
|
||||
self.path = reverse(
|
||||
"openedx.journals.render_xblock_by_journal_access",
|
||||
kwargs={
|
||||
"usage_key_string": "block-v1:edX+DemoX+Demo_Course+type@video+block@5c90cffecd9b48b188cbfea176bf7fe9"
|
||||
}
|
||||
)
|
||||
|
||||
@override_switch(JOURNAL_INTEGRATION, True)
|
||||
@mock.patch('openedx.features.journals.views.journal_xblock.fetch_journal_access')
|
||||
@mock.patch('openedx.features.journals.views.journal_xblock.render_xblock')
|
||||
def test_without_journal_access(self, mocked_render_xblock, mocked_journal_access):
|
||||
"""
|
||||
Test the journal page without journal access.
|
||||
"""
|
||||
mocked_journal_access.return_value = []
|
||||
mocked_render_xblock.return_value = []
|
||||
path = "{path}?journal_uuid={journal_uuid}".format(
|
||||
path=self.path,
|
||||
journal_uuid=str(uuid.uuid4())
|
||||
)
|
||||
response = self.client.get(path=path)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
@override_switch(JOURNAL_INTEGRATION, True)
|
||||
@mock.patch('openedx.features.journals.views.journal_xblock.fetch_journal_access')
|
||||
@mock.patch('openedx.features.journals.views.journal_xblock.render_xblock')
|
||||
def test_unauthenticated_journal_access(self, mocked_render_xblock, mocked_journal_access):
|
||||
"""
|
||||
Test when not logged in
|
||||
"""
|
||||
self.logout()
|
||||
mocked_journal_access.return_value = []
|
||||
mocked_render_xblock.return_value = []
|
||||
path = "{path}?journal_uuid={journal_uuid}".format(
|
||||
path=self.path,
|
||||
journal_uuid=str(uuid.uuid4())
|
||||
)
|
||||
response = self.client.get(path=path)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
@override_switch(JOURNAL_INTEGRATION, True)
|
||||
@mock.patch('openedx.features.journals.views.journal_xblock.fetch_journal_access')
|
||||
@mock.patch('openedx.features.journals.views.journal_xblock.render_xblock')
|
||||
def test_with_journal_access(self, mocked_render_xblock, mocked_journal_access):
|
||||
"""
|
||||
Test the journal page with journal access.
|
||||
"""
|
||||
journal_uuid = str(uuid.uuid4())
|
||||
mocked_journal_access.return_value = get_mocked_journal_access(journal_uuid=journal_uuid)
|
||||
mocked_render_xblock.return_value = HttpResponse("")
|
||||
path = "{path}?journal_uuid={journal_uuid}".format(
|
||||
path=self.path,
|
||||
journal_uuid=journal_uuid
|
||||
)
|
||||
response = self.client.get(path=path)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
mocked_render_xblock.assert_called_once()
|
||||
@@ -25,7 +25,7 @@ def override_switch(switch, active):
|
||||
return decorate
|
||||
|
||||
|
||||
def get_mocked_journal_access():
|
||||
def get_mocked_journal_access(journal_uuid=None):
|
||||
"""
|
||||
Returns the dummy data of journal access
|
||||
"""
|
||||
@@ -35,6 +35,7 @@ def get_mocked_journal_access():
|
||||
"uuid": uuid.uuid4(),
|
||||
"journal": {
|
||||
"name": "dummy-name1",
|
||||
"uuid": journal_uuid if journal_uuid else str(uuid.uuid4()),
|
||||
"organization": "edx",
|
||||
"journalaboutpage": {
|
||||
"id": "5",
|
||||
@@ -47,6 +48,7 @@ def get_mocked_journal_access():
|
||||
"uuid": uuid.uuid4(),
|
||||
"journal": {
|
||||
"name": "dummy-name2",
|
||||
"uuid": str(uuid.uuid4()),
|
||||
"organization": "edx",
|
||||
"journalaboutpage": {
|
||||
"id": "5",
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
Defines URLs for course bookmarks.
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url
|
||||
|
||||
from openedx.features.journals.views.marketing import bundle_about
|
||||
from openedx.features.journals.views import learner_dashboard
|
||||
from openedx.features.journals.views.journal_xblock import render_xblock_by_journal_access
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^bundles/{}/about'.format(r'(?P<bundle_uuid>[0-9a-f-]+)',),
|
||||
@@ -16,4 +18,8 @@ urlpatterns = [
|
||||
learner_dashboard.journal_listing,
|
||||
name='openedx.journals.dashboard'
|
||||
),
|
||||
url(r'^render_journal_block/{usage_key_string}'.format(usage_key_string=settings.USAGE_KEY_PATTERN),
|
||||
render_xblock_by_journal_access,
|
||||
name='openedx.journals.render_xblock_by_journal_access'
|
||||
),
|
||||
]
|
||||
|
||||
66
openedx/features/journals/views/journal_xblock.py
Normal file
66
openedx/features/journals/views/journal_xblock.py
Normal file
@@ -0,0 +1,66 @@
|
||||
"""
|
||||
View for journal page
|
||||
"""
|
||||
import datetime
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
from lms.djangoapps.courseware.views.views import render_xblock
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from openedx.features.journals.api import fetch_journal_access
|
||||
|
||||
XBLOCK_JOURNAL_ACCESS_KEY = "journal_access_for_{username}_{journal_uuid}_{block_id}"
|
||||
|
||||
|
||||
def render_xblock_by_journal_access(request, usage_key_string):
|
||||
"""
|
||||
Its a wrapper function for lms.djangoapps.courseware.views.views.render_xblock.
|
||||
It disables 'check_if_enrolled' flag by checking that user has access on journal.
|
||||
"""
|
||||
block_id = UsageKey.from_string(usage_key_string).block_id
|
||||
user_access = _get_cache_data(request, block_id)
|
||||
if not user_access:
|
||||
raise PermissionDenied()
|
||||
return render_xblock(request, usage_key_string, check_if_enrolled=False)
|
||||
|
||||
|
||||
def _get_cache_data(request, block_id):
|
||||
"""
|
||||
Get the cache data from cache if not then hit the end point
|
||||
in journals to fetch the access of user on given block_id.
|
||||
"""
|
||||
if request.user.is_staff:
|
||||
return True
|
||||
|
||||
if not request.user.is_authenticated:
|
||||
return False
|
||||
|
||||
date_format = '%Y-%m-%d'
|
||||
journal_uuid = request.GET.get('journal_uuid')
|
||||
cache_key = XBLOCK_JOURNAL_ACCESS_KEY.format(
|
||||
username=request.user.username,
|
||||
journal_uuid=journal_uuid,
|
||||
block_id=block_id
|
||||
)
|
||||
user_access = cache.get(cache_key)
|
||||
if user_access is None:
|
||||
journal_access_data = fetch_journal_access(
|
||||
request.site,
|
||||
request.user,
|
||||
block_id=block_id
|
||||
)
|
||||
for journal_access in journal_access_data:
|
||||
if journal_access['journal']['uuid'] == journal_uuid:
|
||||
expiration_date = datetime.datetime.strptime(journal_access['expiration_date'], date_format)
|
||||
now = datetime.datetime.strptime(datetime.datetime.now().strftime(date_format), date_format)
|
||||
if expiration_date >= now:
|
||||
user_access = True
|
||||
|
||||
cache.set(
|
||||
cache_key,
|
||||
user_access,
|
||||
configuration_helpers.get_value("JOURNAL_ACCESS_CACHE_TTL", 3600)
|
||||
)
|
||||
return user_access
|
||||
Reference in New Issue
Block a user