refactor: Update get block OLX view to support versions [FC-0062] (#35932)

* Deprecate `get_block_draft_olx`
* Deprecate get olx view in content libraries
* Create `get_block_olx` in xblock API with support of versions
* Create get olx view in xblock
This commit is contained in:
Chris Chávez
2024-12-05 13:49:23 -05:00
committed by GitHub
parent 60aa299c61
commit 0bd0e6f4ca
7 changed files with 84 additions and 10 deletions

View File

@@ -710,6 +710,9 @@ class LibraryBlockOlxView(APIView):
@convert_exceptions
def get(self, request, usage_key_str):
"""
DEPRECATED. Use get_block_olx_view() in xblock REST-API.
Can be removed post-Teak.
Get the block's OLX
"""
key = LibraryUsageLocatorV2.from_string(usage_key_str)

View File

@@ -33,7 +33,12 @@ from openedx.core.djangoapps.xblock.runtime.learning_core_runtime import (
LearningCoreXBlockRuntime,
)
from .data import CheckPerm, LatestVersion
from .utils import get_secure_token_for_xblock_handler, get_xblock_id_for_anonymous_user
from .rest_api.url_converters import VersionConverter
from .utils import (
get_secure_token_for_xblock_handler,
get_xblock_id_for_anonymous_user,
get_auto_latest_version,
)
from .runtime.learning_core_runtime import LearningCoreXBlockRuntime
@@ -208,13 +213,26 @@ def get_component_from_usage_key(usage_key: UsageKeyV2) -> Component:
)
def get_block_draft_olx(usage_key: UsageKeyV2) -> str:
def get_block_olx(
usage_key: UsageKeyV2,
*,
version: int | LatestVersion = LatestVersion.AUTO
) -> str:
"""
Get the OLX source of the draft version of the given Learning-Core-backed XBlock.
Get the OLX source of the of the given Learning-Core-backed XBlock and a version.
"""
# Inefficient but simple approach. Optimize later if needed.
component = get_component_from_usage_key(usage_key)
component_version = component.versioning.draft
version = get_auto_latest_version(version)
if version == LatestVersion.DRAFT:
component_version = component.versioning.draft
elif version == LatestVersion.PUBLISHED:
component_version = component.versioning.published
else:
assert isinstance(version, int)
component_version = component.versioning.version_num(version)
if component_version is None:
raise NoSuchUsage(usage_key)
# TODO: we should probably make a method on ComponentVersion that returns
# a content based on the name. Accessing by componentversioncontent__key is
@@ -224,6 +242,11 @@ def get_block_draft_olx(usage_key: UsageKeyV2) -> str:
return content.text
def get_block_draft_olx(usage_key: UsageKeyV2) -> str:
""" DEPRECATED. Use get_block_olx(). Can be removed post-Teak. """
return get_block_olx(usage_key, version=LatestVersion.DRAFT)
def render_block_view(block, view_name, user): # pylint: disable=unused-argument
"""
Get the HTML, JS, and CSS needed to render the given XBlock view.

View File

@@ -0,0 +1,11 @@
"""
Serializers for the xblock REST API
"""
from rest_framework import serializers
class XBlockOlxSerializer(serializers.Serializer):
"""
Serializer for representing an XBlock's OLX
"""
olx = serializers.CharField()

View File

@@ -19,6 +19,8 @@ block_endpoints = [
path('', views.block_metadata),
# get/post full json fields of an XBlock:
path('fields/', views.BlockFieldsView.as_view()),
# Get the OLX source code of the specified block
path('olx/', views.get_block_olx_view),
# render one of this XBlock's views (e.g. student_view)
path('view/<str:view_name>/', views.render_block_view),
# get the URL needed to call this XBlock's handlers

View File

@@ -33,9 +33,11 @@ from ..api import (
get_handler_url as _get_handler_url,
load_block,
render_block_view as _render_block_view,
get_block_olx,
)
from ..utils import validate_secure_token_for_xblock_handler
from .url_converters import VersionConverter
from .serializers import XBlockOlxSerializer
User = get_user_model()
@@ -213,6 +215,23 @@ def xblock_handler(
return response
@api_view(['GET'])
@view_auth_classes(is_authenticated=False)
def get_block_olx_view(
request,
usage_key: UsageKeyV2,
version: LatestVersion | int = LatestVersion.AUTO,
):
"""
Get the OLX (XML serialization) of the specified XBlock
"""
context_impl = get_learning_context_impl(usage_key)
if not context_impl.can_view_block_for_editing(request.user, usage_key):
raise PermissionDenied(f"You don't have permission to access the OLX of component '{usage_key}'.")
olx = get_block_olx(usage_key, version=version)
return Response(XBlockOlxSerializer({"olx": olx}).data)
def cors_allow_xblock_handler(sender, request, **kwargs): # lint-amnesty, pylint: disable=unused-argument
"""
Sandboxed XBlocks need to be able to call XBlock handlers via POST,

View File

@@ -24,6 +24,7 @@ from openedx.core.djangoapps.xblock.api import get_xblock_app_config
from openedx.core.lib.xblock_serializer.api import serialize_modulestore_block_for_learning_core
from openedx.core.lib.xblock_serializer.data import StaticFile
from ..data import AuthoredDataMode, LatestVersion
from ..utils import get_auto_latest_version
from ..learning_context.manager import get_learning_context_impl
from .runtime import XBlockRuntime
@@ -178,11 +179,7 @@ class LearningCoreXBlockRuntime(XBlockRuntime):
# just get it the easy way.
component = self._get_component_from_usage_key(usage_key)
if version == LatestVersion.AUTO:
if self.authored_data_mode == AuthoredDataMode.DEFAULT_DRAFT:
version = LatestVersion.DRAFT
else:
version = LatestVersion.PUBLISHED
version = get_auto_latest_version(version)
if self.authored_data_mode == AuthoredDataMode.STRICTLY_PUBLISHED and version != LatestVersion.PUBLISHED:
raise ValidationError("This runtime only allows accessing the published version of components")
if version == LatestVersion.DRAFT:

View File

@@ -11,6 +11,10 @@ from uuid import uuid4
import crum
from django.conf import settings
from openedx.core.djangoapps.xblock.apps import get_xblock_app_config
from .data import AuthoredDataMode, LatestVersion
def get_secure_token_for_xblock_handler(user_id, block_key_str, time_idx=0):
"""
@@ -167,3 +171,18 @@ def get_xblock_id_for_anonymous_user(user):
return current_request.session["xblock_id_for_anonymous_user"]
else:
raise RuntimeError("Cannot get a user ID for an anonymous user outside of an HTTP request context.")
def get_auto_latest_version(version: int | LatestVersion) -> int | LatestVersion:
"""
Gets the actual LatestVersion if is `LatestVersion.AUTO`;
otherwise, returns the same value.
"""
if version == LatestVersion.AUTO:
authored_data_mode = get_xblock_app_config().get_runtime_params()["authored_data_mode"]
version = (
LatestVersion.DRAFT
if authored_data_mode == AuthoredDataMode.DEFAULT_DRAFT
else LatestVersion.PUBLISHED
)
return version