Skip to content

Commit e6b5d31

Browse files
fix: replace content_key with context_key
1 parent b7a9461 commit e6b5d31

3 files changed

Lines changed: 38 additions & 29 deletions

File tree

cms/djangoapps/contentstore/git_export_utils.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from openedx.core.djangoapps.content_libraries.api import extract_library_v2_zip_to_dir
1919
from xmodule.contentstore.django import contentstore
2020
from xmodule.modulestore.django import modulestore
21-
from xmodule.modulestore.xml_exporter import export_course_to_xml, export_library_to_xml
21+
from xmodule.modulestore.xml_exporter import CourseLocator, export_course_to_xml, export_library_to_xml
2222

2323
log = logging.getLogger(__name__)
2424

@@ -68,12 +68,12 @@ def cmd_log(cmd, cwd):
6868
return output
6969

7070

71-
def export_to_git(content_key, repo, user='', rdir=None):
71+
def export_to_git(context_key, repo, user='', rdir=None):
7272
"""
7373
Export a course or library to git.
7474
7575
Args:
76-
content_key: CourseKey or LibraryLocator for the content to export
76+
context_key: LearningContextKey for the content to export
7777
repo (str): Git repository URL
7878
user (str): Optional username for git commit identity
7979
rdir (str): Optional custom directory name for the repository
@@ -83,17 +83,12 @@ def export_to_git(content_key, repo, user='', rdir=None):
8383
"""
8484
# pylint: disable=too-many-statements
8585

86-
# Detect content type and select appropriate export function
87-
content_type_label = "library"
88-
is_library_v2 = isinstance(content_key, LibraryLocatorV2)
89-
if is_library_v2:
90-
# V2 libraries use backup API with zip extraction
91-
content_export_func = extract_library_v2_zip_to_dir
92-
elif isinstance(content_key, LibraryLocator):
93-
content_export_func = export_library_to_xml
94-
else:
95-
content_export_func = export_course_to_xml
96-
content_type_label = "course"
86+
# Validate context_key type and determine export function and content type label
87+
if not isinstance(context_key, (LibraryLocatorV2, LibraryLocator, CourseLocator)):
88+
raise TypeError(
89+
f"{context_key!r} for git export must be LibraryLocatorV2, LibraryLocator, "
90+
f"or CourseLocator, not {type(context_key)}"
91+
)
9792

9893
if not GIT_REPO_EXPORT_DIR:
9994
raise GitExportError(GitExportError.NO_EXPORT_DIR)
@@ -157,12 +152,23 @@ def export_to_git(content_key, repo, user='', rdir=None):
157152
root_dir = os.path.dirname(rdirp)
158153
content_dir = os.path.basename(rdirp).rsplit('.git', 1)[0]
159154

155+
content_type_label = "course" if context_key.is_course else "library"
156+
157+
is_library_v2 = isinstance(context_key, LibraryLocatorV2)
158+
if is_library_v2:
159+
# V2 libraries use backup API with zip extraction
160+
content_export_func = extract_library_v2_zip_to_dir
161+
elif isinstance(context_key, LibraryLocator):
162+
content_export_func = export_library_to_xml
163+
else:
164+
content_export_func = export_course_to_xml
165+
160166
try:
161167
if is_library_v2:
162-
content_export_func(content_key, root_dir, content_dir, user)
168+
content_export_func(context_key, root_dir, content_dir, user)
163169
else:
164170
# V1 libraries and courses: use XML export (no user parameter)
165-
content_export_func(modulestore(), contentstore(), content_key,
171+
content_export_func(modulestore(), contentstore(), context_key,
166172
root_dir, content_dir)
167173
except (OSError, AttributeError):
168174
log.exception('Failed to export %s', content_type_label)
@@ -212,6 +218,6 @@ def export_to_git(content_key, repo, user='', rdir=None):
212218
log.info(
213219
'%s %s exported to git repository %s successfully',
214220
content_type_label.capitalize(),
215-
content_key,
221+
context_key,
216222
repo,
217223
)

openedx/core/djangoapps/content_libraries/api/backup.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
import shutil
88
import zipfile
99
from datetime import datetime
10-
import shutil
1110
from tempfile import mkdtemp
12-
import zipfile
1311

1412
from django.conf import settings
1513
from django.contrib.auth import get_user_model
@@ -41,11 +39,11 @@ def create_library_v2_zip(library_key: LibraryLocatorV2, user) -> tuple:
4139
filename = f'{sanitized_lib_key}-{timestamp}.zip'
4240
file_path = os.path.join(root_dir, filename)
4341
origin_server = getattr(settings, 'CMS_BASE', None)
44-
create_lib_zip_file(lp_key=str(library_key), path=file_path, user=user, origin_server=origin_server)
42+
create_lib_zip_file(package_ref=str(library_key), path=file_path, user=user, origin_server=origin_server)
4543
return root_dir, file_path
4644

4745

48-
def extract_library_v2_zip_to_dir(library_key, root_dir, library_dir, user=None):
46+
def extract_library_v2_zip_to_dir(library_key, root_dir, library_dir, username=None):
4947
"""
5048
Export a v2 library to a directory by creating a zip backup and extracting it.
5149
@@ -57,13 +55,18 @@ def extract_library_v2_zip_to_dir(library_key, root_dir, library_dir, user=None)
5755
library_key: LibraryLocatorV2 for the library to export
5856
root_dir: Root directory where library_dir will be created
5957
library_dir: Directory name under root_dir to extract the library into
60-
user: Username string for the backup API (optional)
58+
username: Username string for the backup API (optional)
6159
6260
Raises:
6361
Exception: If backup creation or extraction fails
62+
DoesNotExist: If the specified user does not exist
6463
"""
65-
# Get user object for backup API
66-
user_obj = get_user_model().objects.filter(username=user).first()
64+
# Get user object for backup API (if username provided)
65+
user_obj = None
66+
if username:
67+
# Let it raise if given user doesn't exist
68+
user_obj = get_user_model().objects.get(username=username)
69+
6770
temp_dir, zip_path = create_library_v2_zip(library_key, user_obj)
6871

6972
try:

openedx/core/djangoapps/content_libraries/api/tests/test_backup.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,15 @@ def test_successful_extraction(self, mock_create_zip, mock_get_user_model):
5050
temp_zip_dir, zip_path = self._make_zip_in_temp_dir({'content.xml': b'<lib/>'})
5151
mock_create_zip.return_value = (temp_zip_dir, zip_path)
5252
mock_user = MagicMock()
53-
mock_get_user_model.return_value.objects.filter.return_value.first.return_value = mock_user
53+
mock_get_user_model.return_value.objects.get.return_value = mock_user
5454

5555
try:
5656
target = root_dir / 'my-library'
5757
assert not target.exists(), "Target dir should not exist before the call"
5858

59-
extract_library_v2_zip_to_dir(LIBRARY_KEY, str(root_dir), 'my-library', user='testuser')
59+
extract_library_v2_zip_to_dir(LIBRARY_KEY, str(root_dir), 'my-library', username='testuser')
6060

61-
mock_get_user_model.return_value.objects.filter.assert_called_once_with(username='testuser')
61+
mock_get_user_model.return_value.objects.get.assert_called_once_with(username='testuser')
6262
mock_create_zip.assert_called_once_with(LIBRARY_KEY, mock_user)
6363
assert target.isdir(), "Target dir should have been created"
6464
assert (target / 'content.xml').exists(), "Zip content should be extracted"
@@ -76,12 +76,12 @@ def test_temp_dir_cleaned_up_even_on_extraction_error(self, mock_create_zip, moc
7676
root_dir = Path(tempfile.mkdtemp())
7777
temp_zip_dir, zip_path = self._make_zip_in_temp_dir()
7878
mock_create_zip.return_value = (temp_zip_dir, zip_path)
79-
mock_get_user_model.return_value.objects.filter.return_value.first.return_value = None
79+
mock_get_user_model.return_value.objects.get.return_value = None
8080

8181
try:
8282
with patch('zipfile.ZipFile.extractall', side_effect=OSError('disk full')):
8383
with pytest.raises(OSError, match='disk full'):
84-
extract_library_v2_zip_to_dir(LIBRARY_KEY, str(root_dir), 'my-library', user=None)
84+
extract_library_v2_zip_to_dir(LIBRARY_KEY, str(root_dir), 'my-library', username=None)
8585
assert not temp_zip_dir.exists(), "Temp dir should be cleaned up on error"
8686
finally:
8787
shutil.rmtree(root_dir, ignore_errors=True)

0 commit comments

Comments
 (0)