diff --git a/docs/001-custom-features.md b/docs/001-custom-features.md index a79a2ff2..cdb6825a 100644 --- a/docs/001-custom-features.md +++ b/docs/001-custom-features.md @@ -31,3 +31,29 @@ This feature allows CCX coaches (ccx_coach in the course access role model) and - Find the assignment and grade it. - After grading the assignment, it should not be necessary to approve the grade. - Go to the progress page, with the student user and check if the grade is assigned to the unit containing the SGA component. + +## Custom Storage Backend + +### Context + +The SGA Xblock in its upstream repository approaches the storage capability by relaying on the Django's default storage +that is defined in the edx platform. There's also a feature where the storage backend to be used can be defined via +platform settings. For our fork, this feature adds the capability to define a site-aware backend via site configurations. + +### Feature + +To define a desired backend, follow the following format by defining the storage class and its kwargs. +As an example, below you will find the Site Configuration definition to use an AWS S3 bucket: + +"SGA_STORAGE_SETTINGS": { + "STORAGE_CLASS": "storages.backends.s3boto3.S3Boto3Storage", + "STORAGE_KWARGS": { + "access_key": "aws_access_key", + "secret_key": "aws_secret_key", + "bucket_name": "aws-bucket-name", + "region_name": "us-east-1" + } +} + +Once it has been done, the SGA xblocks for the given site will manage the media objects with the defined storage backend. +If no settings are defined, the Xblock would use the default Django storage. diff --git a/edx_sga/tests/test_utils.py b/edx_sga/tests/test_utils.py index 48384114..2bd62009 100644 --- a/edx_sga/tests/test_utils.py +++ b/edx_sga/tests/test_utils.py @@ -1,7 +1,7 @@ """ Tests for SGA utility functions """ -from django.test import override_settings +from unittest.mock import patch from django.core.files.storage import default_storage import pytest import pytz @@ -35,23 +35,34 @@ def test_utcnow(): assert is_near_now(now) assert now.tzinfo.zone == pytz.utc.zone -@override_settings(SGA_STORAGE_SETTINGS={ - 'STORAGE_CLASS': 'storages.backends.s3boto3.S3Boto3Storage', - 'STORAGE_KWARGS': - {'bucket_name': 'test', 'location': 'abc/def'} - }) -def test_get_default_storage_with_settings_override(): +patch("edx_sga.utils.configuration_helpers") +def test_get_default_storage_with_settings_override(mock_configuration_helpers): """ get_default_storage should return an S3Boto3Storage object """ + mock_configuration_helpers.return_value.get_value.return_value = ( + { + "SGA_STORAGE_SETTINGS": { + "STORAGE_CLASS": "storages.backends.s3boto3.S3Boto3Storage", + "STORAGE_KWARGS": { + "access_key": "test_key", + "secret_key": "test_secret", + "bucket_name": "test-bucket-1", + "region_name": "us-east-1", + }, + }, + }, + ) storage = get_default_storage() assert storage.__class__ == S3Boto3Storage # make sure kwargs are passed through constructor assert storage.bucket.name == 'test' -def test_get_default_storage_without_settings_override(): +patch("edx_sga.utils.configuration_helpers") +def test_get_default_storage_without_settings_override(mock_configuration_helpers): """ get_default_storage should return default_storage object """ + mock_configuration_helpers.return_value.get_value.return_value = {} storage = get_default_storage() assert storage == default_storage diff --git a/edx_sga/utils.py b/edx_sga/utils.py index 4ca18dd8..8fc11901 100644 --- a/edx_sga/utils.py +++ b/edx_sga/utils.py @@ -8,14 +8,14 @@ from functools import partial import pytz -from django.conf import settings from django.core.files.storage import default_storage as django_default_storage, get_storage_class +from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from edx_sga.constants import BLOCK_SIZE def get_default_storage(): """ - Get config for storage from settings, use Django's default_storage if no such settings are defined + Get config for storage from site configurations, use Django's default_storage if no such settings are defined """ # .. setting_name: SGA_STORAGE_SETTINGS # .. setting_default: {} @@ -25,7 +25,7 @@ def get_default_storage(): # STORAGE_CLASS: 'storage', # STORAGE_KWARGS: {} # } - sga_storage_settings = getattr(settings, "SGA_STORAGE_SETTINGS", None) + sga_storage_settings = configuration_helpers.get_value('SGA_STORAGE_SETTINGS', None) if sga_storage_settings: return get_storage_class(