Skip to content

Commit a295cbd

Browse files
committed
fix: address PR comments other than logging & nits
1 parent bcef3e8 commit a295cbd

File tree

4 files changed

+37
-26
lines changed

4 files changed

+37
-26
lines changed

cms/djangoapps/contentstore/rest_api/v2/views/downstreams.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"developer_message": string?
6262
}
6363
"""
64-
from django.contrib.auth.models import AbstractUser
64+
from django.contrib.auth.models import User # pylint: disable=imported-auth-user
6565
from opaque_keys import InvalidKeyError
6666
from opaque_keys.edx.keys import UsageKey
6767
from rest_framework.exceptions import NotFound, ValidationError
@@ -82,13 +82,23 @@
8282
from xmodule.modulestore.exceptions import ItemNotFoundError
8383

8484

85+
class _AuthenticatedRequest(Request):
86+
"""
87+
Alias for the `Request` class which tells mypy to assume that `.user` is not an AnonymousUser.
88+
89+
Using this does NOT ensure the request is actually authenticated--
90+
you will some other way to ensure that, such as `@view_auth_classes(is_authenticated=True)`.
91+
"""
92+
user: User
93+
94+
8595
# TODO: Potential future view.
8696
# @view_auth_classes(is_authenticated=True)
8797
# class DownstreamListView(DeveloperErrorViewMixin, APIView):
8898
# """
8999
# List all blocks which are linked to upstream content, with optional filtering.
90100
# """
91-
# def get(self, request: Request) -> Response:
101+
# def get(self, request: _AuthenticatedRequest) -> Response:
92102
# """
93103
# Handle the request.
94104
# """
@@ -102,22 +112,20 @@ class DownstreamView(DeveloperErrorViewMixin, APIView):
102112
"""
103113
Inspect or manage an XBlock's link to upstream content.
104114
"""
105-
def get(self, request: Request, usage_key_string: str) -> Response:
115+
def get(self, request: _AuthenticatedRequest, usage_key_string: str) -> Response:
106116
"""
107117
Inspect an XBlock's link to upstream content.
108118
"""
109-
assert isinstance(request.user, AbstractUser)
110119
downstream = _load_accessible_block(request.user, usage_key_string, require_write_access=False)
111120
_ensure_upstream_ref(downstream)
112121
if link := UpstreamLink.try_get_for_block(downstream):
113122
return Response(link.to_json())
114123
raise ValidationError(detail=f"Block '{usage_key_string}' is not linked to an upstream")
115124

116-
def put(self, request: Request, usage_key_string: str) -> Response:
125+
def put(self, request: _AuthenticatedRequest, usage_key_string: str) -> Response:
117126
"""
118127
Edit an XBlock's link to upstream content.
119128
"""
120-
assert isinstance(request.user, AbstractUser)
121129
downstream = _load_accessible_block(request.user, usage_key_string, require_write_access=True)
122130
new_upstream_ref = request.data.get("upstream_ref")
123131
if not isinstance(new_upstream_ref, str):
@@ -140,15 +148,13 @@ def put(self, request: Request, usage_key_string: str) -> Response:
140148
except BadUpstream as exc:
141149
raise ValidationError({"upstream_ref": str(exc)}) from exc
142150
modulestore().update_item(downstream, request.user.id)
143-
link = UpstreamLink.get_for_block(downstream)
144-
assert link
151+
link: UpstreamLink = UpstreamLink.get_for_block(downstream) # type: ignore[assignment]
145152
return Response(link.to_json())
146153

147-
# def delete(self, request: Request, usage_key_string: str) -> Response:
154+
# def delete(self, request: _AuthenticatedRequest, usage_key_string: str) -> Response:
148155
# """
149156
# Sever an XBlock's link to upstream content.
150157
# """
151-
# assert isinstance(request.user, AbstractUser)
152158
# downstream = _load_accessible_block(request.user, usage_key_string, require_write_access=True)
153159
# _ensure_upstream_ref(downstream)
154160
# ....
@@ -160,11 +166,10 @@ class SyncFromUpstreamView(DeveloperErrorViewMixin, APIView):
160166
Accept or decline an opportunity to sync a downstream block from its upstream content.
161167
"""
162168

163-
def post(self, request: Request, usage_key_string: str) -> Response:
169+
def post(self, request: _AuthenticatedRequest, usage_key_string: str) -> Response:
164170
"""
165171
Pull latest updates to the block at {usage_key_string} from its linked upstream content.
166172
"""
167-
assert isinstance(request.user, AbstractUser)
168173
downstream = _load_accessible_block(request.user, usage_key_string, require_write_access=True)
169174
_ensure_upstream_ref(downstream)
170175
if not downstream.upstream:
@@ -175,15 +180,13 @@ def post(self, request: Request, usage_key_string: str) -> Response:
175180
except (BadUpstream, BadDownstream) as exc:
176181
raise ValidationError(detail=str(exc)) from exc
177182
modulestore().update_item(downstream, request.user.id)
178-
upstream_link = UpstreamLink.get_for_block(downstream)
179-
assert upstream_link
180-
return Response(upstream_link.to_json(), status=200)
183+
link: UpstreamLink = UpstreamLink.get_for_block(downstream) # type: ignore[assignment]
184+
return Response(link.to_json(), status=200)
181185

182-
def delete(self, request: Request, usage_key_string: str) -> Response:
186+
def delete(self, request: _AuthenticatedRequest, usage_key_string: str) -> Response:
183187
"""
184188
Decline the latest updates to the block at {usage_key_string}.
185189
"""
186-
assert isinstance(request.user, AbstractUser)
187190
downstream = _load_accessible_block(request.user, usage_key_string, require_write_access=True)
188191
_ensure_upstream_ref(downstream)
189192
try:
@@ -194,7 +197,7 @@ def delete(self, request: Request, usage_key_string: str) -> Response:
194197
return Response(status=204)
195198

196199

197-
def _load_accessible_block(user: AbstractUser, usage_key_string: str, *, require_write_access: bool) -> XBlock:
200+
def _load_accessible_block(user: User, usage_key_string: str, *, require_write_access: bool) -> XBlock:
198201
"""
199202
Given a logged in-user and a serialized usage key of an upstream-linked XBlock, load it from the ModuleStore,
200203
raising a DRF-friendly exception if anything goes wrong.

cms/envs/common.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127

128128
from lms.djangoapps.lms_xblock.mixin import LmsBlockMixin
129129
from cms.lib.xblock.authoring_mixin import AuthoringMixin
130+
from cms.lib.xblock.upstream_sync import UpstreamSyncMixin
130131
from xmodule.modulestore.edit_info import EditInfoMixin
131132
from openedx.core.djangoapps.theming.helpers_dirs import (
132133
get_themes_unchecked,
@@ -995,7 +996,7 @@
995996
XModuleMixin,
996997
EditInfoMixin,
997998
AuthoringMixin,
998-
'cms.lib.xblock.upstream_sync.UpstreamSyncMixin',
999+
UpstreamSyncMixin,
9991000
)
10001001

10011002
# .. setting_name: XBLOCK_EXTRA_MIXINS

cms/lib/xblock/upstream_sync.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
HOWEVER, those assumptions may loosen in the future. So, we consider these to be INTERNAL ASSUMPIONS that should not be
1010
exposed through this module's public Python interface.
1111
"""
12+
from __future__ import annotations
13+
1214
import typing as t
1315
from dataclasses import dataclass, asdict
1416

15-
from django.contrib.auth.models import AbstractUser
1617
from django.core.exceptions import PermissionDenied
1718
from django.utils.translation import gettext_lazy as _
1819
from rest_framework.exceptions import NotFound
@@ -23,8 +24,8 @@
2324
from xblock.fields import Scope, String, Integer
2425
from xblock.core import XBlockMixin, XBlock
2526

26-
import openedx.core.djangoapps.xblock.api as xblock_api
27-
from openedx.core.djangoapps.content_libraries.api import get_library_block
27+
if t.TYPE_CHECKING:
28+
from django.contrib.auth.models import AbstractUser
2829

2930

3031
class BadUpstream(Exception):
@@ -127,6 +128,10 @@ def get_for_block(cls, downstream: XBlock) -> t.Self | None:
127128
f"downstream block '{downstream.usage_key}' is linked to "
128129
f"upstream block of different type '{upstream_key}'"
129130
)
131+
# We import this here b/c UpstreamSyncMixin is used by cms/envs, which loads before the djangoapps are ready.
132+
from openedx.core.djangoapps.content_libraries.api import (
133+
get_library_block # pylint: disable=wrong-import-order
134+
)
130135
try:
131136
lib_meta = get_library_block(upstream_key)
132137
except XBlockNotFoundError as exc:
@@ -188,8 +193,10 @@ def _load_upstream_link_and_block(downstream: XBlock, user: AbstractUser) -> tup
188193
"""
189194
if not (link := UpstreamLink.get_for_block(downstream)): # can raise BadUpstream, BadDownstream
190195
return None
196+
# We import load_block here b/c UpstreamSyncMixin is used by cms/envs, which loads before the djangoapps are ready.
197+
from openedx.core.djangoapps.xblock.api import load_block # pylint: disable=wrong-import-order
191198
try:
192-
lib_block: XBlock = xblock_api.load_block(UsageKey.from_string(downstream.upstream), user)
199+
lib_block: XBlock = load_block(UsageKey.from_string(downstream.upstream), user)
193200
except (NotFound, PermissionDenied) as exc:
194201
raise BadUpstream(_("Linked library item could not be loaded: {}").format(downstream.upstream)) from exc
195202
return link, lib_block

openedx/core/djangoapps/content_libraries/api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,10 @@ def from_component(cls, library_key, component, associated_collections=None):
257257
library_key,
258258
component,
259259
),
260-
display_name=component.versioning.draft.title,
260+
display_name=draft.title,
261261
created=component.created,
262-
modified=component.versioning.draft.created,
263-
draft_version_num=component.versioning.draft.version_num,
262+
modified=draft.created,
263+
draft_version_num=draft.version_num,
264264
published_version_num=published.version_num if published else None,
265265
last_published=None if last_publish_log is None else last_publish_log.published_at,
266266
published_by=published_by,

0 commit comments

Comments
 (0)