feat: add endpoint to get draft version of a library component asset (#35681)
Adds a new Studio-only libraries static asset endpoint at
/library_assets/blocks/{usage_key}. This endpoint will serve assets
only from the Draft branch, and is only available to people who have
read permission to the containing library.
This also moves the existing library asset endpoint that did lookups
by Component Version to /library_assets/component_versions/{uuid}
This change was motivated by the desire to make it easier to make the
editor preview work for images by having a single URL that will
consistently point to the latest version of the asset, rather than
having a new URL after every save (which the Component Version lookup
This commit is contained in:
@@ -219,8 +219,8 @@ class LibrariesEmbedViewTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestM
|
||||
# show up.
|
||||
html = self._embed_block(block_id, version='published')
|
||||
# This is the pattern we're looking for:
|
||||
# <img src="https://localhost:18010/library_assets/b5864c63-e1da-4d48-8c8a-cc718e2f9ad3/static/deer.jpg"/>
|
||||
assert re.search(r'/library_assets/[0-9a-f-]*/static/deer.jpg', html)
|
||||
# <img src="https://{host}/library_assets/component_versions/.../static/deer.jpg"/>
|
||||
assert re.search(r'/library_assets/component_versions/[0-9a-f-]*/static/deer.jpg', html)
|
||||
|
||||
# Now grab the draft version (4), which is going to once again not have
|
||||
# the asset (because we deleted it).
|
||||
|
||||
@@ -123,19 +123,20 @@ class ContentLibrariesComponentVersionAssetTest(ContentLibrariesRestApiTest):
|
||||
block = self._add_block_to_library(library["id"], "html", "html1")
|
||||
self._set_library_block_asset(block["id"], "static/test.svg", SVG_DATA)
|
||||
usage_key = UsageKey.from_string(block["id"])
|
||||
self.usage_key = usage_key
|
||||
self.component = get_component_from_usage_key(usage_key)
|
||||
self.draft_component_version = self.component.versioning.draft
|
||||
|
||||
def test_good_responses(self):
|
||||
get_response = self.client.get(
|
||||
f"/library_assets/{self.draft_component_version.uuid}/static/test.svg"
|
||||
f"/library_assets/component_versions/{self.draft_component_version.uuid}/static/test.svg"
|
||||
)
|
||||
assert get_response.status_code == 200
|
||||
content = b''.join(chunk for chunk in get_response.streaming_content)
|
||||
assert content == SVG_DATA
|
||||
|
||||
good_head_response = self.client.head(
|
||||
f"/library_assets/{self.draft_component_version.uuid}/static/test.svg"
|
||||
f"/library_assets/component_versions/{self.draft_component_version.uuid}/static/test.svg"
|
||||
)
|
||||
assert good_head_response.headers == get_response.headers
|
||||
|
||||
@@ -144,20 +145,20 @@ class ContentLibrariesComponentVersionAssetTest(ContentLibrariesRestApiTest):
|
||||
# Non-existent version...
|
||||
wrong_version_uuid = UUID('11111111-1111-1111-1111-111111111111')
|
||||
response = self.client.get(
|
||||
f"/library_assets/{wrong_version_uuid}/static/test.svg"
|
||||
f"/library_assets/component_versions/{wrong_version_uuid}/static/test.svg"
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
# Non-existent file...
|
||||
response = self.client.get(
|
||||
f"/library_assets/{self.draft_component_version.uuid}/static/missing.svg"
|
||||
f"/library_assets/component_versions/{self.draft_component_version.uuid}/static/missing.svg"
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
# File-like ComponenVersionContent entry that isn't an actually
|
||||
# downloadable file...
|
||||
response = self.client.get(
|
||||
f"/library_assets/{self.draft_component_version.uuid}/block.xml"
|
||||
f"/library_assets/component_versions/{self.draft_component_version.uuid}/block.xml"
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
@@ -165,7 +166,7 @@ class ContentLibrariesComponentVersionAssetTest(ContentLibrariesRestApiTest):
|
||||
"""Anonymous users shouldn't get access to library assets."""
|
||||
self.client.logout()
|
||||
response = self.client.get(
|
||||
f"/library_assets/{self.draft_component_version.uuid}/static/test.svg"
|
||||
f"/library_assets/component_versions/{self.draft_component_version.uuid}/static/test.svg"
|
||||
)
|
||||
assert response.status_code == 403
|
||||
|
||||
@@ -181,6 +182,32 @@ class ContentLibrariesComponentVersionAssetTest(ContentLibrariesRestApiTest):
|
||||
)
|
||||
self.client.login(username="student", password="student-pass")
|
||||
get_response = self.client.get(
|
||||
f"/library_assets/{self.draft_component_version.uuid}/static/test.svg"
|
||||
f"/library_assets/component_versions/{self.draft_component_version.uuid}/static/test.svg"
|
||||
)
|
||||
assert get_response.status_code == 403
|
||||
|
||||
def test_draft_version(self):
|
||||
"""Get draft version of asset"""
|
||||
get_response = self.client.get(
|
||||
f"/library_assets/blocks/{self.usage_key}/static/test.svg"
|
||||
)
|
||||
assert get_response.status_code == 200
|
||||
content = b''.join(chunk for chunk in get_response.streaming_content)
|
||||
assert content == SVG_DATA
|
||||
|
||||
good_head_response = self.client.head(
|
||||
f"/library_assets/blocks/{self.usage_key}/static/test.svg"
|
||||
)
|
||||
assert good_head_response.headers == get_response.headers
|
||||
|
||||
def test_draft_version_404(self):
|
||||
"""Get draft version of asset"""
|
||||
get_response = self.client.get(
|
||||
f"/library_assets/blocks/{self.usage_key}@/static/test.svg"
|
||||
)
|
||||
assert get_response.status_code == 404
|
||||
|
||||
get_response = self.client.get(
|
||||
f"/library_assets/blocks/{self.usage_key}/static/test2.svg"
|
||||
)
|
||||
assert get_response.status_code == 404
|
||||
|
||||
@@ -76,9 +76,17 @@ urlpatterns = [
|
||||
path('pub/jwks/', views.LtiToolJwksView.as_view(), name='lti-pub-jwks'),
|
||||
])),
|
||||
])),
|
||||
path(
|
||||
'library_assets/<uuid:component_version_uuid>/<path:asset_path>',
|
||||
views.component_version_asset,
|
||||
name='library-assets',
|
||||
path('library_assets/', include([
|
||||
path(
|
||||
'component_versions/<uuid:component_version_uuid>/<path:asset_path>',
|
||||
views.component_version_asset,
|
||||
name='library-assets',
|
||||
),
|
||||
path(
|
||||
'blocks/<usage_v2:usage_key>/<path:asset_path>',
|
||||
views.component_draft_asset,
|
||||
name='library-draft-assets',
|
||||
),
|
||||
])
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1237,3 +1237,18 @@ def component_version_asset(request, component_version_uuid, asset_path):
|
||||
content.read_file().chunks(),
|
||||
headers=redirect_response.headers,
|
||||
)
|
||||
|
||||
|
||||
@require_safe
|
||||
def component_draft_asset(request, usage_key, asset_path):
|
||||
"""
|
||||
Serves the draft version of static assets associated with a Library Component.
|
||||
|
||||
See `component_version_asset` for more details
|
||||
"""
|
||||
try:
|
||||
component_version_uuid = api.get_component_from_usage_key(usage_key).versioning.draft.uuid
|
||||
except ObjectDoesNotExist as exc:
|
||||
raise Http404() from exc
|
||||
|
||||
return component_version_asset(request, component_version_uuid, asset_path)
|
||||
|
||||
@@ -377,7 +377,7 @@ class LearningCoreXBlockRuntime(XBlockRuntime):
|
||||
then this method will be called with asset_path="test.png" and should
|
||||
return a URL like:
|
||||
|
||||
http://studio.local.openedx.io:8001/library_assets/cd31871e-a342-4c3f-ba2f-a661bf630996/static/test.png
|
||||
http://studio.local.openedx.io:8001/library_assets/component_versions/cd31871e-a342-4c3f-ba2f-a661bf630996/static/test.png
|
||||
|
||||
If the asset file is not recognized, return None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user