Skip to content

Commit

Permalink
Merge branch 'lytvynenko/changed_link_parsing' of github.com:Inferato…
Browse files Browse the repository at this point in the history
…/edx-platform into lytvynenko/changed_link_parsing
  • Loading branch information
Inferato committed Oct 23, 2023
2 parents d3b6a69 + d6a9099 commit a1996b3
Show file tree
Hide file tree
Showing 197 changed files with 1,311 additions and 626 deletions.
32 changes: 29 additions & 3 deletions cms/djangoapps/contentstore/signals/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
XBLOCK_DUPLICATED,
XBLOCK_PUBLISHED,
)
from openedx.core.lib.events import determine_producer_config_for_signal_and_topic
from openedx_events.event_bus import get_producer
from pytz import UTC

Expand Down Expand Up @@ -164,6 +165,13 @@ def listen_for_course_catalog_info_changed(sender, signal, **kwargs):
"""
Publish COURSE_CATALOG_INFO_CHANGED signals onto the event bus.
"""
# temporary: defer to EVENT_BUS_PRODUCER_CONFIG if present
producer_config_setting = determine_producer_config_for_signal_and_topic(COURSE_CATALOG_INFO_CHANGED,
'course-catalog-info-changed')
if producer_config_setting is True:
log.info("Producing course-catalog-info-changed event via config")
return
log.info("Producing course-catalog-info-changed event via manual send")
get_producer().send(
signal=COURSE_CATALOG_INFO_CHANGED, topic='course-catalog-info-changed',
event_key_field='catalog_info.course_key', event_data={'catalog_info': kwargs['catalog_info']},
Expand All @@ -176,8 +184,14 @@ def listen_for_xblock_published(sender, signal, **kwargs):
"""
Publish XBLOCK_PUBLISHED signals onto the event bus.
"""
# temporary: defer to EVENT_BUS_PRODUCER_CONFIG if present
topic = getattr(settings, "EVENT_BUS_XBLOCK_LIFECYCLE_TOPIC", "course-authoring-xblock-lifecycle")
producer_config_setting = determine_producer_config_for_signal_and_topic(XBLOCK_PUBLISHED, topic)
if producer_config_setting is True:
log.info("Producing xblock-published event via config")
return
if settings.FEATURES.get("ENABLE_SEND_XBLOCK_EVENTS_OVER_BUS"):
topic = getattr(settings, "EVENT_BUS_XBLOCK_LIFECYCLE_TOPIC", "course-authoring-xblock-lifecycle")
log.info("Producing xblock-published event via manual send")
get_producer().send(
signal=XBLOCK_PUBLISHED, topic=topic,
event_key_field='xblock_info.usage_key', event_data={'xblock_info': kwargs['xblock_info']},
Expand All @@ -190,8 +204,14 @@ def listen_for_xblock_deleted(sender, signal, **kwargs):
"""
Publish XBLOCK_DELETED signals onto the event bus.
"""
# temporary: defer to EVENT_BUS_PRODUCER_CONFIG if present
topic = getattr(settings, "EVENT_BUS_XBLOCK_LIFECYCLE_TOPIC", "course-authoring-xblock-lifecycle")
producer_config_setting = determine_producer_config_for_signal_and_topic(XBLOCK_DELETED, topic)
if producer_config_setting is True:
log.info("Producing xblock-deleted event via config")
return
if settings.FEATURES.get("ENABLE_SEND_XBLOCK_EVENTS_OVER_BUS"):
topic = getattr(settings, "EVENT_BUS_XBLOCK_LIFECYCLE_TOPIC", "course-authoring-xblock-lifecycle")
log.info("Producing xblock-deleted event via manual send")
get_producer().send(
signal=XBLOCK_DELETED, topic=topic,
event_key_field='xblock_info.usage_key', event_data={'xblock_info': kwargs['xblock_info']},
Expand All @@ -204,8 +224,14 @@ def listen_for_xblock_duplicated(sender, signal, **kwargs):
"""
Publish XBLOCK_DUPLICATED signals onto the event bus.
"""
# temporary: defer to EVENT_BUS_PRODUCER_CONFIG if present
topic = getattr(settings, "EVENT_BUS_XBLOCK_LIFECYCLE_TOPIC", "course-authoring-xblock-lifecycle")
producer_config_setting = determine_producer_config_for_signal_and_topic(XBLOCK_DUPLICATED, topic)
if producer_config_setting is True:
log.info("Producing xblock-duplicated event via config")
return
if settings.FEATURES.get("ENABLE_SEND_XBLOCK_EVENTS_OVER_BUS"):
topic = getattr(settings, "EVENT_BUS_XBLOCK_LIFECYCLE_TOPIC", "course-authoring-xblock-lifecycle")
log.info("Producing xblock-duplicated event via manual send")
get_producer().send(
signal=XBLOCK_DUPLICATED, topic=topic,
event_key_field='xblock_info.usage_key', event_data={'xblock_info': kwargs['xblock_info']},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def setUp(self):
self.factory = RequestFactory()
self.global_admin = AdminFactory()
self.client = AjaxEnabledTestClient()
self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
self.course_create_rerun_url = reverse('course_handler')
self.course_start = datetime.datetime.utcnow()
self.course_end = self.course_start + datetime.timedelta(days=30)
Expand Down
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/tests/test_course_listing.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ def setUp(self):
super().setUp()
# create and log in a staff user.
# create and log in a non-staff user
self.user = UserFactory()
self.user = UserFactory(password=self.TEST_PASSWORD)
self.factory = RequestFactory()
self.request = self.factory.get('/course')
self.request.user = self.user
self.client = AjaxEnabledTestClient()
self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

def _create_course_with_access_groups(self, course_location, user=None):
"""
Expand Down
4 changes: 2 additions & 2 deletions cms/djangoapps/contentstore/tests/test_course_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1866,10 +1866,10 @@ def _get_course_details_response(self, global_staff):
"""
Return the course details page as either global or non-global staff
"""
user = UserFactory(is_staff=global_staff)
user = UserFactory(is_staff=global_staff, password=self.TEST_PASSWORD)
CourseInstructorRole(self.course.id).add_users(user)

self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)

return self.client.get_html(self.course_details_url)

Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/tests/test_i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def setUp(self):

self.uname = 'testuser'
self.email = 'test+courses@edx.org'
self.password = 'foo'
self.password = self.TEST_PASSWORD

# Create the use so we can log them in.
self.user = UserFactory.create(username=self.uname, email=self.email, password=self.password)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def setUp(self):
# create and log in a staff user.
self.user = UserFactory(is_staff=True)
self.client = AjaxEnabledTestClient()
self.client.login(username=self.user.username, password='test')
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)

# create a course via the view handler to create course
self.course_key = self.store.make_course_key('Org_1', 'Course_1', 'Run_1')
Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def setUp(self):
super().setUp()

self.email = 'a@b.com'
self.pw = 'xyz'
self.pw = 'password1234'
self.username = 'testuser'
self.client = AjaxEnabledTestClient()
# clear the cache so ratelimiting won't affect these tests
Expand Down
10 changes: 5 additions & 5 deletions cms/djangoapps/contentstore/views/tests/test_certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def test_cannot_create_certificate_if_user_has_no_write_permissions(self):
Tests user without write permissions on course should not able to create certificate
"""
user = UserFactory()
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.ajax_post(
self._url(),
data=CERTIFICATE_JSON
Expand Down Expand Up @@ -635,7 +635,7 @@ def test_delete_certificate_without_write_permissions(self, signatory_path):
"""
self._add_course_certificates(count=2, signatory_count=1, asset_path_format=signatory_path)
user = UserFactory()
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.delete(
self._url(cid=1),
content_type="application/json",
Expand All @@ -653,7 +653,7 @@ def test_delete_certificate_without_global_staff_permissions(self, signatory_pat
user = UserFactory()
for role in [CourseInstructorRole, CourseStaffRole]:
role(self.course.id).add_users(user)
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.delete(
self._url(cid=1),
content_type="application/json",
Expand Down Expand Up @@ -681,7 +681,7 @@ def test_update_active_certificate_without_global_staff_permissions(self, signat
user = UserFactory()
for role in [CourseInstructorRole, CourseStaffRole]:
role(self.course.id).add_users(user)
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.put(
self._url(cid=1),
data=json.dumps(cert_data),
Expand Down Expand Up @@ -799,7 +799,7 @@ def test_certificate_activation_without_write_permissions(self, activate, signat
test_url = reverse_course_url('certificate_activation_handler', self.course.id)
self._add_course_certificates(count=1, signatory_count=2, asset_path_format=signatory_path)
user = UserFactory()
self.client.login(username=user.username, password='test')
self.client.login(username=user.username, password=self.TEST_PASSWORD)
response = self.client.post(
test_url,
data=json.dumps({"is_active": activate}),
Expand Down
5 changes: 3 additions & 2 deletions cms/djangoapps/contentstore/views/tests/test_organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ class TestOrganizationListing(TestCase):
"""Verify Organization listing behavior."""
def setUp(self):
super().setUp()
self.staff = UserFactory(is_staff=True)
self.client.login(username=self.staff.username, password='test')
self.password = "password1234"
self.staff = UserFactory(is_staff=True, password=self.password)
self.client.login(username=self.staff.username, password=self.password)
self.org_names_listing_url = reverse('organizations')
self.org_short_names = ["alphaX", "betaX", "orgX"]
for index, short_name in enumerate(self.org_short_names):
Expand Down
16 changes: 8 additions & 8 deletions cms/djangoapps/maintenance/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class TestMaintenanceIndex(ModuleStoreTestCase):
def setUp(self):
super().setUp()
self.user = AdminFactory()
login_success = self.client.login(username=self.user.username, password='test')
login_success = self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
self.assertTrue(login_success)
self.view_url = reverse('maintenance:maintenance_index')

Expand All @@ -56,7 +56,7 @@ class MaintenanceViewTestCase(ModuleStoreTestCase):
def setUp(self):
super().setUp()
self.user = AdminFactory()
login_success = self.client.login(username=self.user.username, password='test')
login_success = self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
self.assertTrue(login_success)

def verify_error_message(self, data, error_message):
Expand Down Expand Up @@ -110,8 +110,8 @@ def test_non_global_staff_access(self, url):
"""
Test that all maintenance app views are not accessible to non-global-staff user.
"""
user = UserFactory(username='test', email='test@example.com', password='test')
login_success = self.client.login(username=user.username, password='test')
user = UserFactory(username='test', email='test@example.com', password=self.TEST_PASSWORD)
login_success = self.client.login(username=user.username, password=self.TEST_PASSWORD)
self.assertTrue(login_success)

response = self.client.get(url)
Expand Down Expand Up @@ -245,13 +245,13 @@ def setUp(self):
self.admin = AdminFactory.create(
email='staff@edx.org',
username='admin',
password='pass'
password=self.TEST_PASSWORD
)
self.client.login(username=self.admin.username, password='pass')
self.client.login(username=self.admin.username, password=self.TEST_PASSWORD)
self.non_staff_user = UserFactory.create(
email='test@edx.org',
username='test',
password='pass'
password=self.TEST_PASSWORD
)

def test_index(self):
Expand Down Expand Up @@ -301,7 +301,7 @@ def _test_403(self, viewname, kwargs=None):
self.assertEqual(response.status_code, 403)

def test_authorization(self):
self.client.login(username=self.non_staff_user, password='pass')
self.client.login(username=self.non_staff_user, password=self.TEST_PASSWORD)
announcement = Announcement.objects.create(content="Test Delete")
announcement.save()

Expand Down
101 changes: 80 additions & 21 deletions cms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@
# Methods to derive settings
_make_mako_template_dirs,
_make_locale_paths,

# Password Validator Settings
AUTH_PASSWORD_VALIDATORS
)
from path import Path as path
from django.urls import reverse_lazy
Expand Down Expand Up @@ -531,6 +534,19 @@
# .. toggle_creation_date: 2023-03-31
# .. toggle_tickets: https://github.com/openedx/edx-platform/pull/32015
'DISABLE_ADVANCED_SETTINGS': False,

# .. toggle_name: FEATURES['ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS']
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
# .. toggle_description: Enables sending xblock lifecycle events over the event bus. Used to create the
# EVENT_BUS_PRODUCER_CONFIG setting
# .. toggle_use_cases: opt_in
# .. toggle_creation_date: 2023-10-10
# .. toggle_target_removal_date: 2023-10-12
# .. toggle_warning: The default may be changed in a later release. See
# https://github.com/openedx/openedx-events/issues/265
# .. toggle_tickets: https://github.com/edx/edx-arch-experiments/issues/381
'ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS': False,
}

# .. toggle_name: ENABLE_COPPA_COMPLIANCE
Expand Down Expand Up @@ -1795,6 +1811,7 @@

# alternative swagger generator for CMS API
'drf_spectacular',
'openedx_events',
]


Expand Down Expand Up @@ -1879,24 +1896,6 @@
EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST = []

#### PASSWORD POLICY SETTINGS #####
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "common.djangoapps.util.password_policy_validators.MinimumLengthValidator",
"OPTIONS": {
"min_length": 2
}
},
{
"NAME": "common.djangoapps.util.password_policy_validators.MaximumLengthValidator",
"OPTIONS": {
"max_length": 75
}
},
]

PASSWORD_POLICY_COMPLIANCE_ROLLOUT_CONFIG = {
'ENFORCE_COMPLIANCE_ON_LOGIN': False
}
Expand Down Expand Up @@ -2789,6 +2788,66 @@
'PREPROCESSING_HOOKS': ['cms.lib.spectacular.cms_api_filter'], # restrict spectacular to CMS API endpoints
}

#### Event bus publishing ####
## Will be more filled out as part of https://github.com/edx/edx-arch-experiments/issues/381
EVENT_BUS_PRODUCER_CONFIG = {}

#### Event bus producing ####
def _should_send_xblock_events(settings):
return settings.FEATURES['ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS']

# .. setting_name: EVENT_BUS_PRODUCER_CONFIG
# .. setting_default: all events disabled
# .. setting_description: Dictionary of event_types mapped to dictionaries of topic to topic-related configuration.
# Each topic configuration dictionary contains
# * `enabled`: a toggle denoting whether the event will be published to the topic. These should be annotated
# according to
# https://edx.readthedocs.io/projects/edx-toggles/en/latest/how_to/documenting_new_feature_toggles.html
# * `event_key_field` which is a period-delimited string path to event data field to use as event key.
# Note: The topic names should not include environment prefix as it will be dynamically added based on
# EVENT_BUS_TOPIC_PREFIX setting.

EVENT_BUS_PRODUCER_CONFIG = {
'org.openedx.content_authoring.course.catalog_info.changed.v1': {
'course-catalog-info-changed':
{'event_key_field': 'catalog_info.course_key',
# .. toggle_name: EVENT_BUS_PRODUCER_CONFIG['org.openedx.content_authoring.course.catalog_info.changed.v1']
# ['course-catalog-info-changed']['enabled']
# .. toggle_implementation: DjangoSetting
# .. toggle_default: False
# .. toggle_description: if enabled, will publish COURSE_CATALOG_INFO_CHANGED events to the event bus on
# the course-catalog-info-changed topics
# .. toggle_warning: The default may be changed in a later release. See
# https://github.com/openedx/openedx-events/issues/265
# .. toggle_use_cases: opt_in
# .. toggle_creation_date: 2023-10-10
'enabled': False},
},
'org.openedx.content_authoring.xblock.published.v1': {
'course-authoring-xblock-lifecycle':
{'event_key_field': 'xblock_info.usage_key', 'enabled': _should_send_xblock_events},
},
'org.openedx.content_authoring.xblock.deleted.v1': {
'course-authoring-xblock-lifecycle':
{'event_key_field': 'xblock_info.usage_key', 'enabled': _should_send_xblock_events},
},
'org.openedx.content_authoring.xblock.duplicated.v1': {
'course-authoring-xblock-lifecycle':
{'event_key_field': 'xblock_info.usage_key', 'enabled': _should_send_xblock_events},
},
# LMS events. These have to be copied over here because lms.common adds some derived entries as well,
# and the derivation fails if the keys are missing. If we ever remove the import of lms.common, we can remove these.
'org.openedx.learning.certificate.created.v1': {
'learning-certificate-lifecycle':
{'event_key_field': 'certificate.course.course_key', 'enabled': False},
},
'org.openedx.learning.certificate.revoked.v1': {
'learning-certificate-lifecycle':
{'event_key_field': 'certificate.course.course_key', 'enabled': False},
},
}


derived_collection_entry('EVENT_BUS_PRODUCER_CONFIG', 'org.openedx.content_authoring.xblock.published.v1',
'course-authoring-xblock-lifecycle', 'enabled')
derived_collection_entry('EVENT_BUS_PRODUCER_CONFIG', 'org.openedx.content_authoring.xblock.duplicated.v1',
'course-authoring-xblock-lifecycle', 'enabled')
derived_collection_entry('EVENT_BUS_PRODUCER_CONFIG', 'org.openedx.content_authoring.xblock.deleted.v1',
'course-authoring-xblock-lifecycle', 'enabled')
1 change: 1 addition & 0 deletions cms/envs/devstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ def should_show_debug_toolbar(request): # lint-amnesty, pylint: disable=missing
# .. toggle_creation_date: 2023-02-21
# .. toggle_warning: For consistency in user experience, keep the value in sync with the setting of the same name
# in the LMS and CMS.
# This will be deprecated in favor of ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS
# .. toggle_tickets: 'https://github.com/openedx/edx-platform/pull/31813'
FEATURES['ENABLE_SEND_XBLOCK_EVENTS_OVER_BUS'] = True
FEATURES['ENABLE_SEND_ENROLLMENT_EVENTS_OVER_BUS'] = True
Expand Down
Loading

0 comments on commit a1996b3

Please sign in to comment.