forked from openedx/edx-platform
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add/remove components to/from a collection
Python API: * Converts UsageKeyV2 object keys into component keys for use with the oel_collections api. * Sends a CONTENT_OBJECT_TAGS_CHANGED for each component added/removed. REST API: * Calls the python API * Receives a collection PK + a list of UsageKeys to add to the collection.
- Loading branch information
1 parent
250434f
commit f4521b5
Showing
4 changed files
with
266 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
""" | ||
|
||
from __future__ import annotations | ||
import ddt | ||
|
||
from openedx_learning.api.authoring_models import Collection | ||
from opaque_keys.edx.locator import LibraryLocatorV2 | ||
|
@@ -15,8 +16,10 @@ | |
URL_PREFIX = '/api/libraries/v2/{lib_key}/' | ||
URL_LIB_COLLECTIONS = URL_PREFIX + 'collections/' | ||
URL_LIB_COLLECTION = URL_LIB_COLLECTIONS + '{collection_id}/' | ||
URL_LIB_COLLECTION_CONTENTS = URL_LIB_COLLECTION + 'contents/' | ||
|
||
|
||
@ddt.ddt | ||
@skip_unless_cms # Content Library Collections REST API is only available in Studio | ||
class ContentLibraryCollectionsViewsTest(ContentLibrariesRestApiTest): | ||
""" | ||
|
@@ -52,6 +55,20 @@ def setUp(self): | |
created_by=self.user, | ||
) | ||
|
||
# Create some library blocks | ||
self.lib1_problem_block = self._add_block_to_library( | ||
self.lib1.library_key, "problem", "problem1", | ||
) | ||
self.lib1_html_block = self._add_block_to_library( | ||
self.lib1.library_key, "html", "html1", | ||
) | ||
self.lib2_problem_block = self._add_block_to_library( | ||
self.lib2.library_key, "problem", "problem2", | ||
) | ||
self.lib2_html_block = self._add_block_to_library( | ||
self.lib2.library_key, "html", "html2", | ||
) | ||
|
||
def test_get_library_collection(self): | ||
""" | ||
Test retrieving a Content Library Collection | ||
|
@@ -254,3 +271,106 @@ def test_delete_library_collection(self): | |
) | ||
|
||
assert resp.status_code == 405 | ||
|
||
def test_get_components(self): | ||
""" | ||
Retrieving components is not supported by the REST API; | ||
use Meilisearch instead. | ||
""" | ||
resp = self.client.get( | ||
URL_LIB_COLLECTION_CONTENTS.format(lib_key=self.lib1.library_key, collection_id=self.col1.id), | ||
) | ||
assert resp.status_code == 405 | ||
|
||
def test_update_components(self): | ||
""" | ||
Test adding and removing components from a collection. | ||
""" | ||
# Add two components to col1 | ||
resp = self.client.patch( | ||
URL_LIB_COLLECTION_CONTENTS.format(lib_key=self.lib1.library_key, collection_id=self.col1.id), | ||
data={ | ||
"usage_keys": [ | ||
self.lib1_problem_block["id"], | ||
self.lib1_html_block["id"], | ||
] | ||
} | ||
) | ||
assert resp.status_code == 200 | ||
assert resp.data == {"count": 2} | ||
|
||
# Remove one of the added components from col1 | ||
resp = self.client.delete( | ||
URL_LIB_COLLECTION_CONTENTS.format(lib_key=self.lib1.library_key, collection_id=self.col1.id), | ||
data={ | ||
"usage_keys": [ | ||
self.lib1_problem_block["id"], | ||
] | ||
} | ||
) | ||
assert resp.status_code == 200 | ||
assert resp.data == {"count": 1} | ||
|
||
@ddt.data("patch", "delete") | ||
def test_update_components_wrong_collection(self, method): | ||
""" | ||
Collection must belong to the requested library. | ||
""" | ||
resp = getattr(self.client, method)( | ||
URL_LIB_COLLECTION_CONTENTS.format(lib_key=self.lib2.library_key, collection_id=self.col1.id), | ||
data={ | ||
"usage_keys": [ | ||
self.lib1_problem_block["id"], | ||
] | ||
} | ||
) | ||
assert resp.status_code == 404 | ||
|
||
@ddt.data("patch", "delete") | ||
def test_update_components_missing_data(self, method): | ||
""" | ||
List of component keys must contain at least one item. | ||
""" | ||
resp = getattr(self.client, method)( | ||
URL_LIB_COLLECTION_CONTENTS.format(lib_key=self.lib2.library_key, collection_id=self.col3.id), | ||
) | ||
assert resp.status_code == 400 | ||
assert resp.data == { | ||
"usage_keys": ["This field is required."], | ||
} | ||
|
||
@ddt.data("patch", "delete") | ||
def test_update_components_from_another_library(self, method): | ||
""" | ||
Adding/removing components from another library raises a validation error. | ||
""" | ||
resp = getattr(self.client, method)( | ||
URL_LIB_COLLECTION_CONTENTS.format(lib_key=self.lib2.library_key, collection_id=self.col3.id), | ||
data={ | ||
"usage_keys": [ | ||
self.lib1_problem_block["id"], | ||
self.lib1_html_block["id"], | ||
] | ||
} | ||
) | ||
assert resp.status_code == 400 | ||
assert resp.data == { | ||
"usage_keys": "Component(s) not found in library", | ||
} | ||
|
||
@ddt.data("patch", "delete") | ||
def test_update_components_permissions(self, method): | ||
""" | ||
Check that a random user without permissions cannot update a Content Library Collection's components. | ||
""" | ||
random_user = UserFactory.create(username="Random", email="[email protected]") | ||
with self.as_user(random_user): | ||
resp = getattr(self.client, method)( | ||
URL_LIB_COLLECTION_CONTENTS.format(lib_key=self.lib1.library_key, collection_id=self.col1.id), | ||
) | ||
assert resp.status_code == 403 | ||
|
||
resp = self.client.patch( | ||
URL_LIB_COLLECTION_CONTENTS.format(lib_key=self.lib1.library_key, collection_id=self.col1.id), | ||
) | ||
assert resp.status_code == 403 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters