feat: updates legacy libraries list API to include migration info [FC-0097] (#37286)
Adds migration info like `migrated_to_title`, `migrated_to_key` and `is_migrated` fields indicating whether the legacy library was migrated to library v2. If yes, it includes the new library name and key. Users can also filter by migration status using `is_migrated` query param.
This commit is contained in:
@@ -4,9 +4,8 @@ API Serializers for course home
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from openedx.core.lib.api.serializers import CourseKeyField
|
||||
|
||||
from cms.djangoapps.contentstore.rest_api.serializers.common import CourseCommonSerializer
|
||||
from openedx.core.lib.api.serializers import CourseKeyField
|
||||
|
||||
|
||||
class UnsucceededCourseSerializer(serializers.Serializer):
|
||||
@@ -29,6 +28,26 @@ class LibraryViewSerializer(serializers.Serializer):
|
||||
org = serializers.CharField()
|
||||
number = serializers.CharField()
|
||||
can_edit = serializers.BooleanField()
|
||||
is_migrated = serializers.SerializerMethodField()
|
||||
migrated_to_title = serializers.CharField(
|
||||
source="migrations__target__title",
|
||||
required=False
|
||||
)
|
||||
migrated_to_key = serializers.CharField(
|
||||
source="migrations__target__key",
|
||||
required=False
|
||||
)
|
||||
migrated_to_collection_key = serializers.CharField(
|
||||
source="migrations__target_collection__key",
|
||||
required=False
|
||||
)
|
||||
migrated_to_collection_title = serializers.CharField(
|
||||
source="migrations__target_collection__title",
|
||||
required=False
|
||||
)
|
||||
|
||||
def get_is_migrated(self, obj):
|
||||
return "migrations__target__key" in obj
|
||||
|
||||
|
||||
class CourseHomeTabSerializer(serializers.Serializer):
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
|
||||
import edx_api_doc_tools as apidocs
|
||||
from django.conf import settings
|
||||
from organizations import api as org_api
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from organizations import api as org_api
|
||||
|
||||
from openedx.core.lib.api.view_utils import view_auth_classes
|
||||
|
||||
from ....utils import get_home_context, get_course_context, get_library_context
|
||||
from ..serializers import StudioHomeSerializer, CourseHomeTabSerializer, LibraryTabSerializer
|
||||
from ....utils import get_course_context, get_home_context, get_library_context
|
||||
from ..serializers import CourseHomeTabSerializer, LibraryTabSerializer, StudioHomeSerializer
|
||||
|
||||
|
||||
@view_auth_classes(is_authenticated=True)
|
||||
@@ -184,7 +185,17 @@ class HomePageLibrariesView(APIView):
|
||||
"org",
|
||||
apidocs.ParameterLocation.QUERY,
|
||||
description="Query param to filter by course org",
|
||||
)],
|
||||
),
|
||||
apidocs.query_parameter(
|
||||
"is_migrated",
|
||||
bool,
|
||||
description=(
|
||||
"Query param to filter by migrated status of library."
|
||||
" If present (true or false), it will filter by migration status"
|
||||
" else it will return all legacy libraries."
|
||||
),
|
||||
)
|
||||
],
|
||||
responses={
|
||||
200: LibraryTabSerializer,
|
||||
401: "The requester is not authenticated.",
|
||||
@@ -197,6 +208,13 @@ class HomePageLibrariesView(APIView):
|
||||
**Example Request**
|
||||
|
||||
GET /api/contentstore/v1/home/libraries
|
||||
# Returns all legacy libraries
|
||||
|
||||
GET /api/contentstore/v1/home/libraries?is_migrated=true
|
||||
# Returns legacy libraries that were migrated to library v2
|
||||
|
||||
GET /api/contentstore/v1/home/libraries?is_migrated=false
|
||||
# Returns legacy libraries that were not migrated to library v2
|
||||
|
||||
**Response Values**
|
||||
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
"""
|
||||
Unit tests for home page view.
|
||||
"""
|
||||
import ddt
|
||||
import pytz
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import ddt
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.test import override_settings
|
||||
from django.urls import reverse
|
||||
from opaque_keys.edx.locator import LibraryLocatorV2
|
||||
from openedx_learning.api import authoring as authoring_api
|
||||
from organizations.tests.factories import OrganizationFactory
|
||||
from rest_framework import status
|
||||
|
||||
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
|
||||
from cms.djangoapps.contentstore.tests.test_libraries import LibraryTestCase
|
||||
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
|
||||
from cms.djangoapps.modulestore_migrator import api as migrator_api
|
||||
from cms.djangoapps.modulestore_migrator.data import CompositionLevel, RepeatHandlingStrategy
|
||||
from cms.djangoapps.modulestore_migrator.tests.factories import ModulestoreSourceFactory
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
from openedx.core.djangoapps.content_libraries import api as lib_api
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -131,7 +139,6 @@ class HomePageCoursesViewTest(CourseTestCase):
|
||||
}
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
print(response.data)
|
||||
self.assertDictEqual(expected_response, response.data)
|
||||
|
||||
def test_home_page_response_with_api_v2(self):
|
||||
@@ -246,23 +253,121 @@ class HomePageLibrariesViewTest(LibraryTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# Create an additional legacy library
|
||||
self.lib_key_1 = self._create_library(library="lib1")
|
||||
self.organization = OrganizationFactory()
|
||||
|
||||
# Create a new v2 library
|
||||
self.lib_key_v2 = LibraryLocatorV2.from_string(
|
||||
f"lib:{self.organization.short_name}:test-key"
|
||||
)
|
||||
lib_api.create_library(
|
||||
org=self.organization,
|
||||
slug=self.lib_key_v2.slug,
|
||||
title="Test Library",
|
||||
)
|
||||
library = lib_api.ContentLibrary.objects.get(slug=self.lib_key_v2.slug)
|
||||
learning_package = library.learning_package
|
||||
# Create a migration source for the legacy library
|
||||
self.source = ModulestoreSourceFactory(key=self.lib_key_1)
|
||||
self.url = reverse("cms.djangoapps.contentstore:v1:libraries")
|
||||
# Create a collection to migrate this library to
|
||||
collection_key = "test-collection"
|
||||
authoring_api.create_collection(
|
||||
learning_package_id=learning_package.id,
|
||||
key=collection_key,
|
||||
title="Test Collection",
|
||||
created_by=self.user.id,
|
||||
)
|
||||
|
||||
# Migrate self.lib_key_1 to self.lib_key_v2
|
||||
migrator_api.start_migration_to_library(
|
||||
user=self.user,
|
||||
source_key=self.source.key,
|
||||
target_library_key=self.lib_key_v2,
|
||||
target_collection_slug=collection_key,
|
||||
composition_level=CompositionLevel.Component.value,
|
||||
repeat_handling_strategy=RepeatHandlingStrategy.Skip.value,
|
||||
preserve_url_slugs=True,
|
||||
forward_source_to_target=False,
|
||||
)
|
||||
|
||||
def test_home_page_libraries_response(self):
|
||||
"""Check successful response content"""
|
||||
response = self.client.get(self.url)
|
||||
|
||||
expected_response = {
|
||||
"libraries": [{
|
||||
'display_name': 'Test Library',
|
||||
'library_key': 'library-v1:org+lib',
|
||||
'url': '/library/library-v1:org+lib',
|
||||
'org': 'org',
|
||||
'number': 'lib',
|
||||
'can_edit': True
|
||||
}],
|
||||
"libraries": [
|
||||
{
|
||||
'display_name': 'Test Library',
|
||||
'library_key': 'library-v1:org+lib',
|
||||
'url': '/library/library-v1:org+lib',
|
||||
'org': 'org',
|
||||
'number': 'lib',
|
||||
'can_edit': True,
|
||||
'is_migrated': False,
|
||||
},
|
||||
# Second legacy library was migrated so it will include
|
||||
# migrated_to_title and migrated_to_key as well
|
||||
{
|
||||
'display_name': 'Test Library',
|
||||
'library_key': 'library-v1:org+lib1',
|
||||
'url': '/library/library-v1:org+lib1',
|
||||
'org': 'org',
|
||||
'number': 'lib1',
|
||||
'can_edit': True,
|
||||
'is_migrated': True,
|
||||
'migrated_to_title': 'Test Library',
|
||||
'migrated_to_key': 'lib:name0:test-key',
|
||||
'migrated_to_collection_key': 'test-collection',
|
||||
'migrated_to_collection_title': 'Test Collection',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
print(response.data)
|
||||
self.assertDictEqual(expected_response, response.data)
|
||||
self.assertDictEqual(expected_response, response.json())
|
||||
|
||||
# Fetch legacy libraries that were migrated to v2
|
||||
response = self.client.get(self.url + '?is_migrated=true')
|
||||
|
||||
expected_response = {
|
||||
"libraries": [
|
||||
{
|
||||
'display_name': 'Test Library',
|
||||
'library_key': 'library-v1:org+lib1',
|
||||
'url': '/library/library-v1:org+lib1',
|
||||
'org': 'org',
|
||||
'number': 'lib1',
|
||||
'can_edit': True,
|
||||
'is_migrated': True,
|
||||
'migrated_to_title': 'Test Library',
|
||||
'migrated_to_key': 'lib:name0:test-key',
|
||||
'migrated_to_collection_key': 'test-collection',
|
||||
'migrated_to_collection_title': 'Test Collection',
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertDictEqual(expected_response, response.json())
|
||||
|
||||
# Fetch legacy libraries that were not migrated to v2
|
||||
response = self.client.get(self.url + '?is_migrated=false')
|
||||
|
||||
expected_response = {
|
||||
"libraries": [
|
||||
{
|
||||
'display_name': 'Test Library',
|
||||
'library_key': 'library-v1:org+lib',
|
||||
'url': '/library/library-v1:org+lib',
|
||||
'org': 'org',
|
||||
'number': 'lib',
|
||||
'can_edit': True,
|
||||
'is_migrated': False,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertDictEqual(expected_response, response.json())
|
||||
|
||||
@@ -26,12 +26,13 @@ from lti_consumer.models import CourseAllowPIISharingInLTIFlag
|
||||
from milestones import api as milestones_api
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey, UsageKey, UsageKeyV2
|
||||
from opaque_keys.edx.locator import LibraryContainerLocator, LibraryLocator, BlockUsageLocator
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, LibraryContainerLocator, LibraryLocator
|
||||
from openedx_events.content_authoring.data import DuplicatedXBlockData
|
||||
from openedx_events.content_authoring.signals import XBLOCK_DUPLICATED
|
||||
from openedx_events.learning.data import CourseNotificationData
|
||||
from openedx_events.learning.signals import COURSE_NOTIFICATION_REQUESTED
|
||||
from pytz import UTC
|
||||
from rest_framework.fields import BooleanField
|
||||
from xblock.fields import Scope
|
||||
|
||||
from cms.djangoapps.contentstore.toggles import (
|
||||
@@ -61,6 +62,7 @@ from cms.djangoapps.contentstore.toggles import (
|
||||
)
|
||||
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
|
||||
from cms.djangoapps.models.settings.course_metadata import CourseMetadata
|
||||
from cms.djangoapps.modulestore_migrator.api import get_migration_info
|
||||
from common.djangoapps.course_action_state.managers import CourseActionStateItemNotFoundError
|
||||
from common.djangoapps.course_action_state.models import CourseRerunState, CourseRerunUIStateManager
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
@@ -87,8 +89,8 @@ from common.djangoapps.util.milestones_helpers import (
|
||||
from common.djangoapps.xblock_django.api import deprecated_xblocks
|
||||
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
|
||||
from openedx.core import toggles as core_toggles
|
||||
from openedx.core.djangoapps.content_libraries.api import get_container
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.content_libraries.api import get_container
|
||||
from openedx.core.djangoapps.content_tagging.toggles import is_tagging_feature_disabled
|
||||
from openedx.core.djangoapps.credit.api import get_credit_requirements, is_credit_course
|
||||
from openedx.core.djangoapps.discussions.config.waffle import ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND
|
||||
@@ -1584,12 +1586,12 @@ def get_library_context(request, request_is_json=False):
|
||||
It is used for both DRF and django views.
|
||||
"""
|
||||
from cms.djangoapps.contentstore.views.course import (
|
||||
_accessible_libraries_iter,
|
||||
_format_library_for_view,
|
||||
_get_course_creator_status,
|
||||
get_allowed_organizations,
|
||||
get_allowed_organizations_for_libraries,
|
||||
user_can_create_organizations,
|
||||
_accessible_libraries_iter,
|
||||
_get_course_creator_status,
|
||||
_format_library_for_view,
|
||||
)
|
||||
from cms.djangoapps.contentstore.views.library import (
|
||||
user_can_view_create_library_button,
|
||||
@@ -1598,9 +1600,22 @@ def get_library_context(request, request_is_json=False):
|
||||
user_can_create_library,
|
||||
)
|
||||
|
||||
libraries = _accessible_libraries_iter(request.user) if libraries_v1_enabled() else []
|
||||
libraries = list(_accessible_libraries_iter(request.user) if libraries_v1_enabled() else [])
|
||||
library_keys = [lib.location.library_key for lib in libraries]
|
||||
migration_info = get_migration_info(library_keys)
|
||||
is_migrated_filter = request.GET.get('is_migrated', None)
|
||||
data = {
|
||||
'libraries': [_format_library_for_view(lib, request) for lib in libraries],
|
||||
'libraries': [
|
||||
_format_library_for_view(
|
||||
lib,
|
||||
request,
|
||||
migrated_to=migration_info.get(lib.location.library_key)
|
||||
)
|
||||
for lib in libraries
|
||||
if is_migrated_filter is None or (
|
||||
BooleanField().to_internal_value(is_migrated_filter) == (lib.location.library_key in migration_info)
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
if not request_is_json:
|
||||
@@ -1716,9 +1731,7 @@ def get_home_context(request, no_course=False):
|
||||
get_allowed_organizations,
|
||||
get_allowed_organizations_for_libraries,
|
||||
user_can_create_organizations,
|
||||
_accessible_libraries_iter,
|
||||
_get_course_creator_status,
|
||||
_format_library_for_view,
|
||||
)
|
||||
from cms.djangoapps.contentstore.views.library import (
|
||||
user_can_view_create_library_button,
|
||||
|
||||
@@ -7,7 +7,7 @@ import logging
|
||||
import random
|
||||
import re
|
||||
import string
|
||||
from typing import Dict
|
||||
from typing import Dict, NamedTuple, Optional
|
||||
|
||||
import django.utils
|
||||
from ccx_keys.locator import CCXLocator
|
||||
@@ -669,7 +669,7 @@ def library_listing(request):
|
||||
return render_to_response('index.html', data)
|
||||
|
||||
|
||||
def _format_library_for_view(library, request):
|
||||
def _format_library_for_view(library, request, migrated_to: Optional[NamedTuple]):
|
||||
"""
|
||||
Return a dict of the data which the view requires for each library
|
||||
"""
|
||||
@@ -681,6 +681,7 @@ def _format_library_for_view(library, request):
|
||||
'org': library.display_org_with_default,
|
||||
'number': library.display_number_with_default,
|
||||
'can_edit': has_studio_write_access(request.user, library.location.library_key),
|
||||
**(migrated_to._asdict() if migrated_to is not None else {}),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"""
|
||||
API for migration from modulestore to learning core
|
||||
"""
|
||||
from opaque_keys.edx.locator import LibraryLocatorV2
|
||||
from opaque_keys.edx.keys import LearningContextKey
|
||||
from openedx_learning.api.authoring import get_collection
|
||||
from celery.result import AsyncResult
|
||||
from opaque_keys.edx.keys import CourseKey, LearningContextKey
|
||||
from opaque_keys.edx.locator import LibraryLocator, LibraryLocatorV2
|
||||
from openedx_learning.api.authoring import get_collection
|
||||
from user_tasks.models import UserTaskStatus
|
||||
|
||||
from openedx.core.djangoapps.content_libraries.api import get_library
|
||||
from openedx.core.types.user import AuthUser
|
||||
@@ -13,9 +14,10 @@ from . import tasks
|
||||
from .data import RepeatHandlingStrategy
|
||||
from .models import ModulestoreSource
|
||||
|
||||
|
||||
__all__ = (
|
||||
"start_migration_to_library",
|
||||
"is_successfully_migrated",
|
||||
"get_migration_info",
|
||||
)
|
||||
|
||||
|
||||
@@ -56,3 +58,32 @@ def start_migration_to_library(
|
||||
preserve_url_slugs=preserve_url_slugs,
|
||||
forward_source_to_target=forward_source_to_target,
|
||||
)
|
||||
|
||||
|
||||
def is_successfully_migrated(source_key: CourseKey | LibraryLocator) -> bool:
|
||||
"""
|
||||
Check if the source course/library has been migrated successfully.
|
||||
"""
|
||||
return ModulestoreSource.objects.get_or_create(key=str(source_key))[0].migrations.filter(
|
||||
task_status__state=UserTaskStatus.SUCCEEDED
|
||||
).exists()
|
||||
|
||||
|
||||
def get_migration_info(source_keys: list[CourseKey | LibraryLocator]) -> dict:
|
||||
"""
|
||||
Check if the source course/library has been migrated successfully and return target info
|
||||
"""
|
||||
return {
|
||||
info.key: info
|
||||
for info in ModulestoreSource.objects.filter(
|
||||
migrations__task_status__state=UserTaskStatus.SUCCEEDED, key__in=source_keys
|
||||
)
|
||||
.values_list(
|
||||
'migrations__target__key',
|
||||
'migrations__target__title',
|
||||
'migrations__target_collection__key',
|
||||
'migrations__target_collection__title',
|
||||
'key',
|
||||
named=True,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
Test cases for the modulestore migrator API.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from opaque_keys.edx.locator import LibraryLocatorV2
|
||||
from openedx_learning.api import authoring as authoring_api
|
||||
from organizations.tests.factories import OrganizationFactory
|
||||
import pytest
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from cms.djangoapps.contentstore.tests.test_libraries import LibraryTestCase
|
||||
from cms.djangoapps.modulestore_migrator import api
|
||||
from cms.djangoapps.modulestore_migrator.data import CompositionLevel, RepeatHandlingStrategy
|
||||
from cms.djangoapps.modulestore_migrator.models import ModulestoreMigration
|
||||
from cms.djangoapps.modulestore_migrator.tests.factories import ModulestoreSourceFactory
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from openedx.core.djangoapps.content_libraries import api as lib_api
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestModulestoreMigratorAPI(ModuleStoreTestCase):
|
||||
class TestModulestoreMigratorAPI(LibraryTestCase):
|
||||
"""
|
||||
Test cases for the modulestore migrator API.
|
||||
"""
|
||||
@@ -26,16 +26,16 @@ class TestModulestoreMigratorAPI(ModuleStoreTestCase):
|
||||
super().setUp()
|
||||
|
||||
self.organization = OrganizationFactory()
|
||||
self.lib_key = LibraryLocatorV2.from_string(
|
||||
self.lib_key_v2 = LibraryLocatorV2.from_string(
|
||||
f"lib:{self.organization.short_name}:test-key"
|
||||
)
|
||||
lib_api.create_library(
|
||||
org=self.organization,
|
||||
slug=self.lib_key.slug,
|
||||
slug=self.lib_key_v2.slug,
|
||||
title="Test Library",
|
||||
)
|
||||
self.library = lib_api.ContentLibrary.objects.get(slug=self.lib_key.slug)
|
||||
self.learning_package = self.library.learning_package
|
||||
self.library_v2 = lib_api.ContentLibrary.objects.get(slug=self.lib_key_v2.slug)
|
||||
self.learning_package = self.library_v2.learning_package
|
||||
|
||||
def test_start_migration_to_library(self):
|
||||
"""
|
||||
@@ -47,7 +47,7 @@ class TestModulestoreMigratorAPI(ModuleStoreTestCase):
|
||||
api.start_migration_to_library(
|
||||
user=user,
|
||||
source_key=source.key,
|
||||
target_library_key=self.library.library_key,
|
||||
target_library_key=self.library_v2.library_key,
|
||||
target_collection_slug=None,
|
||||
composition_level=CompositionLevel.Component.value,
|
||||
repeat_handling_strategy=RepeatHandlingStrategy.Skip.value,
|
||||
@@ -84,7 +84,7 @@ class TestModulestoreMigratorAPI(ModuleStoreTestCase):
|
||||
api.start_migration_to_library(
|
||||
user=user,
|
||||
source_key=source.key,
|
||||
target_library_key=self.library.library_key,
|
||||
target_library_key=self.library_v2.library_key,
|
||||
target_collection_slug=collection_key,
|
||||
composition_level=CompositionLevel.Component.value,
|
||||
repeat_handling_strategy=RepeatHandlingStrategy.Skip.value,
|
||||
@@ -106,10 +106,43 @@ class TestModulestoreMigratorAPI(ModuleStoreTestCase):
|
||||
api.start_migration_to_library(
|
||||
user=user,
|
||||
source_key=source.key,
|
||||
target_library_key=self.library.library_key,
|
||||
target_library_key=self.library_v2.library_key,
|
||||
target_collection_slug=None,
|
||||
composition_level=CompositionLevel.Component.value,
|
||||
repeat_handling_strategy=RepeatHandlingStrategy.Fork.value,
|
||||
preserve_url_slugs=True,
|
||||
forward_source_to_target=False,
|
||||
)
|
||||
|
||||
def test_get_migration_info(self):
|
||||
"""
|
||||
Test that the API can retrieve migration info.
|
||||
"""
|
||||
user = UserFactory()
|
||||
|
||||
collection_key = "test-collection"
|
||||
authoring_api.create_collection(
|
||||
learning_package_id=self.learning_package.id,
|
||||
key=collection_key,
|
||||
title="Test Collection",
|
||||
created_by=user.id,
|
||||
)
|
||||
|
||||
api.start_migration_to_library(
|
||||
user=user,
|
||||
source_key=self.lib_key,
|
||||
target_library_key=self.library_v2.library_key,
|
||||
target_collection_slug=collection_key,
|
||||
composition_level=CompositionLevel.Component.value,
|
||||
repeat_handling_strategy=RepeatHandlingStrategy.Skip.value,
|
||||
preserve_url_slugs=True,
|
||||
forward_source_to_target=True,
|
||||
)
|
||||
with self.assertNumQueries(1):
|
||||
result = api.get_migration_info([self.lib_key])
|
||||
row = result.get(self.lib_key)
|
||||
assert row is not None
|
||||
assert row.migrations__target__key == str(self.lib_key_v2)
|
||||
assert row.migrations__target__title == "Test Library"
|
||||
assert row.migrations__target_collection__key == collection_key
|
||||
assert row.migrations__target_collection__title == "Test Collection"
|
||||
|
||||
Reference in New Issue
Block a user