diff --git a/.github/workflows/pylint-checks.yml b/.github/workflows/pylint-checks.yml index f1e3d02bba..43677f6bc1 100644 --- a/.github/workflows/pylint-checks.yml +++ b/.github/workflows/pylint-checks.yml @@ -17,7 +17,7 @@ jobs: - module-name: lms-1 path: "lms/djangoapps/badges/ lms/djangoapps/branding/ lms/djangoapps/bulk_email/ lms/djangoapps/bulk_enroll/ lms/djangoapps/bulk_user_retirement/ lms/djangoapps/ccx/ lms/djangoapps/certificates/ lms/djangoapps/commerce/ lms/djangoapps/course_api/ lms/djangoapps/course_blocks/ lms/djangoapps/course_home_api/ lms/djangoapps/course_wiki/ lms/djangoapps/coursewarehistoryextended/ lms/djangoapps/debug/ lms/djangoapps/courseware/ lms/djangoapps/course_goals/ lms/djangoapps/rss_proxy/ lms/djangoapps/save_for_later/" - module-name: lms-2 - path: "lms/djangoapps/gating/ lms/djangoapps/grades/ lms/djangoapps/instructor/ lms/djangoapps/instructor_analytics/ lms/djangoapps/discussion/ lms/djangoapps/edxnotes/ lms/djangoapps/email_marketing/ lms/djangoapps/experiments/ lms/djangoapps/instructor_task/ lms/djangoapps/learner_dashboard/ lms/djangoapps/lms_initialization/ lms/djangoapps/lms_xblock/ lms/djangoapps/lti_provider/ lms/djangoapps/mailing/ lms/djangoapps/mobile_api/ lms/djangoapps/monitoring/ lms/djangoapps/ora_staff_grader/ lms/djangoapps/program_enrollments/ lms/djangoapps/rss_proxy lms/djangoapps/static_template_view/ lms/djangoapps/staticbook/ lms/djangoapps/support/ lms/djangoapps/survey/ lms/djangoapps/teams/ lms/djangoapps/tests/ lms/djangoapps/user_tours/ lms/djangoapps/verify_student/ lms/envs/ lms/lib/ lms/tests.py" + path: "lms/djangoapps/gating/ lms/djangoapps/grades/ lms/djangoapps/instructor/ lms/djangoapps/instructor_analytics/ lms/djangoapps/discussion/ lms/djangoapps/edxnotes/ lms/djangoapps/email_marketing/ lms/djangoapps/experiments/ lms/djangoapps/instructor_task/ lms/djangoapps/learner_dashboard/ lms/djangoapps/lms_initialization/ lms/djangoapps/lms_xblock/ lms/djangoapps/lti_provider/ lms/djangoapps/mailing/ lms/djangoapps/mobile_api/ lms/djangoapps/monitoring/ lms/djangoapps/ora_staff_grader/ lms/djangoapps/program_enrollments/ lms/djangoapps/rss_proxy lms/djangoapps/static_template_view/ lms/djangoapps/staticbook/ lms/djangoapps/support/ lms/djangoapps/survey/ lms/djangoapps/teams/ lms/djangoapps/tests/ lms/djangoapps/user_tours/ lms/djangoapps/verify_student/ lms/djangoapps/mfe_api/ lms/envs/ lms/lib/ lms/tests.py" - module-name: openedx-1 path: "openedx/core/types/ openedx/core/djangoapps/ace_common/ openedx/core/djangoapps/agreements/ openedx/core/djangoapps/api_admin/ openedx/core/djangoapps/auth_exchange/ openedx/core/djangoapps/bookmarks/ openedx/core/djangoapps/cache_toolbox/ openedx/core/djangoapps/catalog/ openedx/core/djangoapps/ccxcon/ openedx/core/djangoapps/commerce/ openedx/core/djangoapps/common_initialization/ openedx/core/djangoapps/common_views/ openedx/core/djangoapps/config_model_utils/ openedx/core/djangoapps/content/ openedx/core/djangoapps/content_libraries/ openedx/core/djangoapps/contentserver/ openedx/core/djangoapps/cookie_metadata/ openedx/core/djangoapps/cors_csrf/ openedx/core/djangoapps/course_apps/ openedx/core/djangoapps/course_date_signals/ openedx/core/djangoapps/course_groups/ openedx/core/djangoapps/courseware_api/ openedx/core/djangoapps/crawlers/ openedx/core/djangoapps/credentials/ openedx/core/djangoapps/credit/ openedx/core/djangoapps/dark_lang/ openedx/core/djangoapps/debug/ openedx/core/djangoapps/demographics/ openedx/core/djangoapps/discussions/ openedx/core/djangoapps/django_comment_common/ openedx/core/djangoapps/embargo/ openedx/core/djangoapps/enrollments/ openedx/core/djangoapps/external_user_ids/ openedx/core/djangoapps/zendesk_proxy/ openedx/core/djangolib/ openedx/core/lib/ openedx/core/tests/ openedx/core/djangoapps/course_live/" - module-name: openedx-2 diff --git a/.github/workflows/unit-test-shards.json b/.github/workflows/unit-test-shards.json index 15d5ce270e..c63ce0f97c 100644 --- a/.github/workflows/unit-test-shards.json +++ b/.github/workflows/unit-test-shards.json @@ -71,6 +71,7 @@ "lms/djangoapps/tests/", "lms/djangoapps/user_tours/", "lms/djangoapps/verify_student/", + "lms/djangoapps/mfe_api/", "lms/envs/", "lms/lib/", "lms/tests.py" diff --git a/lms/djangoapps/mfe_api/__init__.py b/lms/djangoapps/mfe_api/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/mfe_api/tests/__init__.py b/lms/djangoapps/mfe_api/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/mfe_api/tests/test_views.py b/lms/djangoapps/mfe_api/tests/test_views.py new file mode 100644 index 0000000000..2b28bd87fc --- /dev/null +++ b/lms/djangoapps/mfe_api/tests/test_views.py @@ -0,0 +1,43 @@ +""" +Test the use cases of the views of the mfe api. +""" + +from unittest.mock import patch + +from django.conf import settings +from django.urls import reverse +from rest_framework import status + +from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers +from openedx.core.lib.api.test_utils import ApiTestCase + + +class MFEConfigTestCase(ApiTestCase): + """ + Test the use case that exposes the site configuration with the mfe api. + """ + def setUp(self): + self.mfe_config_api_url = reverse('mfe_api:config') + return super().setUp() + + def test_get_mfe_config(self): + """Test the get mfe config from site configuration with the mfe api. + + Expected result: + - Inside self.get_json pass the response is a json and the status is 200 asserts. + - The configuration obtained by the api is equal to its site configuration in the + MFE_CONFIG key. + """ + mfe_config = configuration_helpers.get_value('MFE_CONFIG', {}) + response_json = self.get_json(self.mfe_config_api_url) + assert response_json == mfe_config + + @patch.dict(settings.FEATURES, {'ENABLE_MFE_API': False}) + def test_404_get_mfe_config(self): + """Test the 404 not found response from get mfe config. + + Expected result: + - Response status code equal to 404 + """ + response = self.client.get(self.mfe_config_api_url) + assert response.status_code == status.HTTP_404_NOT_FOUND diff --git a/lms/djangoapps/mfe_api/urls.py b/lms/djangoapps/mfe_api/urls.py new file mode 100644 index 0000000000..5cc5152345 --- /dev/null +++ b/lms/djangoapps/mfe_api/urls.py @@ -0,0 +1,10 @@ +""" URLs configuration for the mfe api.""" + +from django.urls import path + +from lms.djangoapps.mfe_api.views import MFEConfigView + +app_name = 'mfe_api' +urlpatterns = [ + path('v1/config', MFEConfigView.as_view(), name='config'), +] diff --git a/lms/djangoapps/mfe_api/views.py b/lms/djangoapps/mfe_api/views.py new file mode 100644 index 0000000000..40764b4e6c --- /dev/null +++ b/lms/djangoapps/mfe_api/views.py @@ -0,0 +1,42 @@ +""" +MFE API Views for useful information related to mfes. +""" + +from django.conf import settings +from django.http import JsonResponse +from django.utils.decorators import method_decorator +from django.views.decorators.cache import cache_page +from rest_framework import status +from rest_framework.views import APIView + +from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers + + +class MFEConfigView(APIView): + """ + Provides an API endpoint to get the MFE_CONFIG from site configuration. + """ + + @method_decorator(cache_page(settings.MFE_API_CONFIG_CACHE_TIMEOUT)) + def get(self, request): + """ + GET /api/mfe/v1/config + + **GET Response Values** + ``` + { + "LOGO_URL": "https://example.com/logo.png", + } + ``` + """ + + if not settings.FEATURES.get('ENABLE_MFE_API'): + msg = 'MFE API not found. Try setting FEATURES["ENABLE_MFE_API"] to true.' + return JsonResponse({'message': msg}, status=status.HTTP_404_NOT_FOUND) + + mfe_config = {'MFE_CONFIG': configuration_helpers.get_value('MFE_CONFIG', {})} + if request.query_params.get('mfe'): + mfe = str(request.query_params.get('mfe')).upper() + mfe_config[f'MFE_CONFIG_{mfe}']= configuration_helpers.get_value(f'MFE_CONFIG_{mfe}',{}) + + return JsonResponse(mfe_config, status=status.HTTP_200_OK) diff --git a/lms/envs/common.py b/lms/envs/common.py index d61433ed9e..0934236dc1 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -1018,6 +1018,18 @@ FEATURES = { # .. toggle_target_removal_date: None # .. toggle_tickets: 'https://openedx.atlassian.net/browse/MST-1458' 'ENABLE_CERTIFICATES_IDV_REQUIREMENT': False, + + # .. toggle_name: FEATURES['ENABLE_MFE_API'] + # .. toggle_implementation: DjangoSetting + # .. toggle_default: False + # .. toggle_description: Set to True to enable MFE Config REST API. This is disabled by + # default. + # .. toggle_use_cases: open_edx + # .. toggle_creation_date: 2022-05-20 + # .. toggle_target_removal_date: None + # .. toggle_warnings: None + # .. toggle_tickets: None + 'ENABLE_MFE_API': False, } # Specifies extra XBlock fields that should available when requested via the Course Blocks API @@ -3251,6 +3263,9 @@ INSTALLED_APPS = [ # Blockstore 'blockstore.apps.bundles', + + # MFE API + 'lms.djangoapps.mfe_api', ] ######################### CSRF ######################################### @@ -4909,6 +4924,12 @@ HIBP_LOGIN_BLOCK_PASSWORD_FREQUENCY_THRESHOLD = 5 # .. toggle_tickets: https://openedx.atlassian.net/browse/VAN-838 ENABLE_DYNAMIC_REGISTRATION_FIELDS = False +# .. setting_name: MFE_API_CONFIG_CACHE_TIMEOUT +# .. setting_default: 60*5 +# .. setting_description: The MFE_CONFIG site configuration will be cached during the +# specified time +MFE_API_CONFIG_CACHE_TIMEOUT = 60 * 5 + ############### Settings for the ace_common plugin ################# # Note that all settings are actually defined by the plugin # pylint: disable=wrong-import-position diff --git a/lms/envs/production.py b/lms/envs/production.py index fabcc024ef..b1b0b6bc6e 100644 --- a/lms/envs/production.py +++ b/lms/envs/production.py @@ -1078,3 +1078,6 @@ COURSE_LIVE_GLOBAL_CREDENTIALS["BIG_BLUE_BUTTON"] = { "SECRET": ENV_TOKENS.get('BIG_BLUE_BUTTON_GLOBAL_SECRET', None), "URL": ENV_TOKENS.get('BIG_BLUE_BUTTON_GLOBAL_URL', None), } + +############################# MFE API CONFIG CACHE TIMEOUT############################ +MFE_API_CONFIG_CACHE_TIMEOUT = ENV_TOKENS.get('MFE_API_CONFIG_CACHE_TIMEOUT', MFE_API_CONFIG_CACHE_TIMEOUT) diff --git a/lms/envs/test.py b/lms/envs/test.py index edef277574..4186d98e45 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -647,3 +647,6 @@ COURSE_LIVE_GLOBAL_CREDENTIALS["BIG_BLUE_BUTTON"] = { "SECRET": "***", "URL": "***", } + +################## MFE API #################### +FEATURES['ENABLE_MFE_API'] = True diff --git a/lms/urls.py b/lms/urls.py index 352a25dee5..974e14bc27 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -1022,3 +1022,8 @@ urlpatterns += [ urlpatterns += [ path('api/instructor_task/', include('lms.djangoapps.instructor_task.rest_api.urls')), ] + +# MFE API urls +urlpatterns += [ + path('api/mfe/', include(('lms.djangoapps.mfe_api.urls', 'lms.djangoapps.mfe_api'), namespace='mfe_api')) +]