From 948f8537d10408c1b0fd6adbb78f3a5be4e82977 Mon Sep 17 00:00:00 2001 From: Hamzah Ullah Date: Tue, 8 Sep 2026 12:43:56 -0400 Subject: [PATCH] feat: replace enterprise support contact-tag import with filter call Swaps the direct openedx.features.enterprise_support import in the support contact-us view for a call to the SupportContactContextRequested openedx-filter. Per pwnage101's review feedback, the filter call is made after context['tags'] = tags and operates on the whole page context dict rather than a bare tags list, since it's the context (not just the tags) that's conceptually being requested; the unused user argument is dropped entirely since pipeline steps that need it fetch it via crum internally. No settings changes here: OPEN_EDX_FILTERS_CONFIG registration for the pipeline step lives in edx-enterprise's own plugin_settings() (enterprise/settings/common.py), per the ENT-11830 ownership handoff. The test now mocks the filter call at the view layer; the enterprise-specific pipeline step behavior is covered by edx-enterprise's own test suite. Also bumps openedx-filters (3.10.0 -> 3.11.0, carrying the context-shape change above) and edx-enterprise (8.10.0 -> 8.11.0, carrying the matching SupportContactEnterpriseTagStep update) in pyproject.toml's uv_constraints and uv.lock / the compiled requirements files, now that both releases are published on PyPI. Verified locally against the real released packages (not just the mocked unit test) that SupportContactContextRequested.run_filter accepts and returns a bare context dict as expected. ENT-11574 --- lms/djangoapps/support/tests/test_views.py | 45 ++++++++++++++++++++++ lms/djangoapps/support/views/contact_us.py | 12 +++--- pyproject.toml | 4 +- requirements/edx/base.txt | 4 +- requirements/edx/development.txt | 4 +- uv.lock | 14 +++---- 6 files changed, 63 insertions(+), 20 deletions(-) diff --git a/lms/djangoapps/support/tests/test_views.py b/lms/djangoapps/support/tests/test_views.py index 7e2e04ea1f68..86bee237172e 100644 --- a/lms/djangoapps/support/tests/test_views.py +++ b/lms/djangoapps/support/tests/test_views.py @@ -96,6 +96,51 @@ def setUp(self): assert success, 'Could not log in' +class ContactUsViewTests(SupportViewTestCase): + """ + Tests for ContactUsView. + """ + + @override_settings(ZENDESK_URL='https://example.zendesk.com') + @patch('lms.djangoapps.support.views.contact_us.SupportContactContextRequested.run_filter') + def test_tags_run_through_filter_for_authenticated_user(self, mock_run_filter): + """ + For an authenticated user, the page context (including tags) is passed through the + SupportContactContextRequested filter, and the filter's return value is used as the + final context for the rendered page. + + The behavior of the filter's pipeline step (edx-enterprise's SupportContactEnterpriseTagStep) + is covered by edx-enterprise's own test suite. This view only needs to verify it wires + the filter's return value through correctly. + """ + def fake_run_filter(context): + return {**context, 'tags': [*context['tags'], 'enterprise_learner']} + + mock_run_filter.side_effect = fake_run_filter + + response = self.client.get(reverse('support:contact_us')) + + assert response.status_code == 200 + mock_run_filter.assert_called_once() + _, call_kwargs = mock_run_filter.call_args + assert call_kwargs['context']['tags'] == ['LMS'] + assert b'enterprise_learner' in response.content + + def test_filter_not_called_for_anonymous_user(self): + """ + Anonymous users never reach the enterprise-tagging branch. + """ + self.client.logout() + with override_settings(ZENDESK_URL='https://example.zendesk.com'): + with patch( + 'lms.djangoapps.support.views.contact_us.SupportContactContextRequested.run_filter' + ) as mock_run_filter: + response = self.client.get(reverse('support:contact_us')) + + assert response.status_code == 200 + mock_run_filter.assert_not_called() + + class SupportViewManageUserTests(SupportViewTestCase): """ Base class for support view tests. diff --git a/lms/djangoapps/support/views/contact_us.py b/lms/djangoapps/support/views/contact_us.py index acb1dc122e62..8be993f91c51 100644 --- a/lms/djangoapps/support/views/contact_us.py +++ b/lms/djangoapps/support/views/contact_us.py @@ -7,11 +7,11 @@ from django.http import Http404 from django.shortcuts import redirect from django.views.generic import View +from openedx_filters.learning.filters import SupportContactContextRequested from common.djangoapps.edxmako.shortcuts import marketing_link, render_to_response from common.djangoapps.student.models import CourseEnrollment from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers -from openedx.features.enterprise_support import api as enterprise_api class ContactUsView(View): @@ -36,21 +36,19 @@ def get(self, request): # pylint: disable=missing-function-docstring } # Tag all issues with LMS to distinguish channel which received the request - tags = ['LMS'] + context['tags'] = ['LMS'] # Per edX support, we would like to be able to route feedback items by site via tagging current_site_name = configuration_helpers.get_value("SITE_NAME") if current_site_name: current_site_name = current_site_name.replace(".", "_") - tags.append(f"site_name_{current_site_name}") + context['tags'].append(f"site_name_{current_site_name}") if request.user.is_authenticated: context['course_id'] = request.session.get('course_id', '') context['user_enrollments'] = CourseEnrollment.enrollments_for_user_with_overviews_preload(request.user) - enterprise_customer = enterprise_api.enterprise_customer_for_request(request) - if enterprise_customer: - tags.append('enterprise_learner') - context['tags'] = tags + if request.user.is_authenticated: + context = SupportContactContextRequested.run_filter(context=context) return render_to_response("support/contact_us.html", context) diff --git a/pyproject.toml b/pyproject.toml index 29b214f7ccd3..43d8bcf43350 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -290,7 +290,7 @@ constraint-dependencies = [ "sphinx-autoapi<3.6.1", "setuptools<82", "astroid==4.0.4", - "edx-enterprise==8.9.4", + "edx-enterprise==8.11.0", "djangorestframework<3.18", ] [tool.edx_lint] @@ -407,7 +407,7 @@ uv_constraints = [ # The team that owns this package will manually bump this package rather than # having it pulled in automatically. This is to allow them to better control its # deployment and to do it in a process that works better for them. - "edx-enterprise==8.9.4", + "edx-enterprise==8.11.0", # Date: 2026-08-31 # DRF 3.18.0 changes many=True validation errors from a list to a dict keyed by # item index, which breaks the error response shape of several write endpoints. diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index aafbc395d366..21e80f3dcce8 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -480,7 +480,7 @@ edx-drf-extensions==10.8.0 # openedx-authz # openedx-core # openedx-platform -edx-enterprise==8.9.4 +edx-enterprise==8.11.0 # via openedx-platform edx-event-bus-kafka==6.1.0 # via openedx-platform @@ -845,7 +845,7 @@ openedx-events==11.2.0 # openedx-core # openedx-platform # ora2 -openedx-filters==3.9.0 +openedx-filters==3.11.0 # via # edx-enterprise # lti-consumer-xblock diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 9fb980ed12e9..0cf2b9f78980 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -533,7 +533,7 @@ edx-drf-extensions==10.8.0 # openedx-authz # openedx-core # openedx-platform -edx-enterprise==8.9.4 +edx-enterprise==8.11.0 # via openedx-platform edx-event-bus-kafka==6.1.0 # via openedx-platform @@ -947,7 +947,7 @@ openedx-events==11.2.0 # openedx-core # openedx-platform # ora2 -openedx-filters==3.9.0 +openedx-filters==3.11.0 # via # edx-enterprise # lti-consumer-xblock diff --git a/uv.lock b/uv.lock index d9e58461dc59..1c43a24d90f4 100644 --- a/uv.lock +++ b/uv.lock @@ -20,7 +20,7 @@ constraints = [ { name = "django-oauth-toolkit", specifier = "==1.7.1" }, { name = "django-stubs", specifier = "<6" }, { name = "djangorestframework", specifier = "<3.18" }, - { name = "edx-enterprise", specifier = "==8.9.4" }, + { name = "edx-enterprise", specifier = "==8.11.0" }, { name = "elasticsearch", specifier = "==7.9.1" }, { name = "libsass", specifier = "==0.10.0" }, { name = "lxml", specifier = "==5.3.2" }, @@ -2044,7 +2044,7 @@ wheels = [ [[package]] name = "edx-enterprise" -version = "8.9.4" +version = "8.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bleach" }, @@ -2098,9 +2098,9 @@ dependencies = [ { name = "tincan" }, { name = "unicodecsv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3e/3a/7b57ce0e6781268bab6035b0a85b769a01e13fdfd231de7a447e51eaf7c0/edx_enterprise-8.9.4.tar.gz", hash = "sha256:22ff6461888a496bdeaba57623cb58ea6114cefa4479d7bf821a281be75a4db1", size = 5166016, upload-time = "2026-08-28T13:08:06.996Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/25/659c1ce1ee742721ddb8063da436bffed1a5b3891fbdf5e8bea5b289c601/edx_enterprise-8.11.0.tar.gz", hash = "sha256:e059a570463f01214555d431d467191e261a9f8d3ac5bce15161b4beefd48e9e", size = 5166614, upload-time = "2026-09-10T18:04:36.466Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/ac/a44afffd844843be1241faa1a8de145e83d9cb284da026c7b248268fcfa0/edx_enterprise-8.9.4-py3-none-any.whl", hash = "sha256:ab81432141cc106e9319e76be9da9516f7acb3f577635016355f58b6ad8d2ce8", size = 5559792, upload-time = "2026-08-28T13:08:03.667Z" }, + { url = "https://files.pythonhosted.org/packages/22/d9/c867215a28e0ce0335710a574ec3da5632c9e299dba7cc82f1a646aac69d/edx_enterprise-8.11.0-py3-none-any.whl", hash = "sha256:49138decfadf5d5ee84854bc4e19840c74fde635ba8cc33b0d8eb57fb876c5f9", size = 5560621, upload-time = "2026-09-10T18:04:33.04Z" }, ] [[package]] @@ -4370,7 +4370,7 @@ wheels = [ [[package]] name = "openedx-filters" -version = "3.9.0" +version = "3.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django", version = "4.2.30", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-16-openedx-platform-django42'" }, @@ -4378,9 +4378,9 @@ dependencies = [ { name = "edx-opaque-keys" }, { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/54/34ff71ff7ac30feff9b76f4961b1ad1561010d2b218afb30c44ac2e81361/openedx_filters-3.9.0.tar.gz", hash = "sha256:928c159c9d215172f82cc894a06ada805b7a0c53a3ab1d148efefa1209efe4d5", size = 51210, upload-time = "2026-08-07T00:39:07.819Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/c7/91104a834a10a8dddb6cf5f60b93e55b0f749adb187f3322a00a2f460f72/openedx_filters-3.11.0.tar.gz", hash = "sha256:4fe0815a8e7edf45e34f3edeb2d8653446b1c3136e57d06109f5303964898f79", size = 51483, upload-time = "2026-09-10T18:50:26.047Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/98/eccd5dd4e25996209ddb623c74c219f0961c92cd6fed1b630483bc872039/openedx_filters-3.9.0-py2.py3-none-any.whl", hash = "sha256:a29bfd0c897d53f5567bf6205ffbb3ea2688aacbbb9c3f5071010d3ab3534412", size = 50085, upload-time = "2026-08-07T00:39:06.47Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/71128665dfd5860caafa3977493ab61b2f08aab80a453d951053799c58f8/openedx_filters-3.11.0-py2.py3-none-any.whl", hash = "sha256:e2b86d324b3d16b728af64fe1e6f8796f97d21b9de9e2356806545efda28e08b", size = 50461, upload-time = "2026-09-10T18:50:24.901Z" }, ] [[package]]