feat: Send events when update containers [FC-0090] (#36866)

* Send `LIBRARY_CONTAINER_UPDATED` for containers that contains the updated unit/section when calling update_container
This commit is contained in:
Chris Chávez
2025-06-09 11:09:16 -05:00
committed by GitHub
parent 0b48f2795a
commit a274db0912
2 changed files with 195 additions and 73 deletions

View File

@@ -304,6 +304,7 @@ def update_container(
container_type = ContainerType(container_key.container_type)
version: ContainerVersion
affected_containers: list[ContainerMetadata] = []
match container_type:
case ContainerType.Unit:
@@ -313,6 +314,7 @@ def update_container(
created=created,
created_by=user_id,
)
affected_containers = get_containers_contains_item(container_key)
case ContainerType.Subsection:
version = authoring_api.create_next_subsection_version(
container.subsection,
@@ -320,6 +322,7 @@ def update_container(
created=created,
created_by=user_id,
)
affected_containers = get_containers_contains_item(container_key)
case ContainerType.Section:
version = authoring_api.create_next_section_version(
container.section,
@@ -327,15 +330,28 @@ def update_container(
created=created,
created_by=user_id,
)
# The `affected_containers` are not obtained, because the sections are
# not contained in any container.
case _:
raise NotImplementedError(f"Library does not support {container_type} yet")
# Send event related to the updated container
LIBRARY_CONTAINER_UPDATED.send_event(
library_container=LibraryContainerData(
container_key=container_key,
)
)
# Send events related to the containers that contains the updated container.
# This is to update the children display names used in the section/subsection previews.
for affected_container in affected_containers:
LIBRARY_CONTAINER_UPDATED.send_event(
library_container=LibraryContainerData(
container_key=affected_container.container_key,
)
)
return ContainerMetadata.from_container(library_key, version.container)

View File

@@ -335,7 +335,8 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
assert collection.created_by == self.user
assert event_receiver.call_count == 1
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_COLLECTION_CREATED,
"sender": None,
@@ -346,7 +347,6 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
),
),
},
event_receiver.call_args_list[0].kwargs,
)
def test_create_library_collection_invalid_library(self) -> None:
@@ -373,7 +373,8 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
assert self.col1.created_by == self.user
assert event_receiver.call_count == 1
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_COLLECTION_UPDATED,
"sender": None,
@@ -384,7 +385,6 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
),
),
},
event_receiver.call_args_list[0].kwargs,
)
def test_update_library_collection_wrong_library(self) -> None:
@@ -406,7 +406,8 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
)
assert event_receiver.call_count == 1
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_COLLECTION_DELETED,
"sender": None,
@@ -417,7 +418,6 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
),
),
},
event_receiver.call_args_list[0].kwargs,
)
def test_update_library_collection_items(self) -> None:
@@ -463,7 +463,8 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
)
assert event_receiver.call_count == 4
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[0].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -472,9 +473,9 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
changes=["collections"],
),
},
event_receiver.call_args_list[0].kwargs,
)
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[1].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -483,9 +484,9 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
changes=["collections"],
),
},
event_receiver.call_args_list[1].kwargs,
)
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[2].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -494,9 +495,9 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
changes=["collections"],
),
},
event_receiver.call_args_list[2].kwargs,
)
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[3].kwargs,
{
"signal": LIBRARY_COLLECTION_UPDATED,
"sender": None,
@@ -507,7 +508,6 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
),
),
},
event_receiver.call_args_list[3].kwargs,
)
def test_update_collection_components_from_wrong_library(self) -> None:
@@ -539,14 +539,18 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
assert self.lib2.learning_package_id is not None
assert len(authoring_api.get_collection(self.lib2.learning_package_id, self.col2.key).entities.all()) == 1
assert len(authoring_api.get_collection(self.lib2.learning_package_id, self.col3.key).entities.all()) == 1
assert {
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
"content_object": ContentObjectChangedData(
object_id=self.lib2_problem_block["id"],
changes=["collections"],
),
}.items() <= event_receiver.call_args_list[0].kwargs.items()
self.assertDictContainsEntries(
event_receiver.call_args_list[0].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
"content_object": ContentObjectChangedData(
object_id=self.lib2_problem_block["id"],
changes=["collections"],
),
},
)
assert len(collection_update_event_receiver.call_args_list) == 2
collection_update_events = [call.kwargs for call in collection_update_event_receiver.call_args_list]
@@ -578,7 +582,8 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
api.delete_library_block(LibraryUsageLocatorV2.from_string(self.lib1_problem_block["id"]))
assert event_receiver.call_count == 1
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_COLLECTION_UPDATED,
"sender": None,
@@ -590,7 +595,6 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
background=True,
),
},
event_receiver.call_args_list[0].kwargs,
)
def test_delete_library_container(self) -> None:
@@ -610,7 +614,8 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
api.delete_container(LibraryContainerLocator.from_string(self.unit1["id"]))
assert event_receiver.call_count == 1
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_COLLECTION_UPDATED,
"sender": None,
@@ -622,7 +627,6 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
background=True,
),
},
event_receiver.call_args_list[0].kwargs,
)
def test_restore_library_block(self) -> None:
@@ -641,7 +645,8 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
api.restore_library_block(LibraryUsageLocatorV2.from_string(self.lib1_problem_block["id"]))
assert event_receiver.call_count == 1
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_COLLECTION_UPDATED,
"sender": None,
@@ -653,7 +658,6 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
background=True,
),
},
event_receiver.call_args_list[0].kwargs,
)
def test_add_component_and_revert(self) -> None:
@@ -681,16 +685,19 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
api.revert_changes(self.lib1.library_key)
assert collection_update_event_receiver.call_count == 1
assert {
"signal": LIBRARY_COLLECTION_UPDATED,
"sender": None,
"library_collection": LibraryCollectionData(
collection_key=api.library_collection_locator(
self.lib1.library_key,
collection_key=self.col1.key,
self.assertDictContainsEntries(
collection_update_event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_COLLECTION_UPDATED,
"sender": None,
"library_collection": LibraryCollectionData(
collection_key=api.library_collection_locator(
self.lib1.library_key,
collection_key=self.col1.key,
),
),
),
}.items() <= collection_update_event_receiver.call_args_list[0].kwargs.items()
},
)
def test_delete_component_and_revert(self) -> None:
"""
@@ -717,16 +724,19 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest):
api.revert_changes(self.lib1.library_key)
assert collection_update_event_receiver.call_count == 1
assert {
"signal": LIBRARY_COLLECTION_UPDATED,
"sender": None,
"library_collection": LibraryCollectionData(
collection_key=api.library_collection_locator(
self.lib1.library_key,
collection_key=self.col1.key,
self.assertDictContainsEntries(
collection_update_event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_COLLECTION_UPDATED,
"sender": None,
"library_collection": LibraryCollectionData(
collection_key=api.library_collection_locator(
self.lib1.library_key,
collection_key=self.col1.key,
),
),
),
}.items() <= collection_update_event_receiver.call_args_list[0].kwargs.items()
},
)
class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
@@ -865,7 +875,8 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
using the `LIBRARY_CONTAINER_UPDATED` signal.
"""
assert event_mock.call_count == 2
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_mock.call_args_list[0].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
@@ -874,9 +885,9 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
background=True,
)
},
event_mock.call_args_list[0].kwargs,
)
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_mock.call_args_list[1].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
@@ -885,7 +896,6 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
background=True,
)
},
event_mock.call_args_list[1].kwargs,
)
def test_call_container_update_signal_when_delete_component(self) -> None:
@@ -920,6 +930,97 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
self._set_library_block_fields(self.html_block_usage_key, {"data": block_olx, "metadata": {}})
self._validate_calls_of_html_block(container_update_event_receiver)
def test_call_container_update_signal_when_update_unit(self) -> None:
container_update_event_receiver = mock.Mock()
LIBRARY_CONTAINER_UPDATED.connect(container_update_event_receiver)
self._update_container(self.unit1.container_key, 'New Unit Display Name')
assert container_update_event_receiver.call_count == 3
self.assertDictContainsEntries(
container_update_event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
"library_container": LibraryContainerData(
container_key=self.unit1.container_key,
)
},
)
self.assertDictContainsEntries(
container_update_event_receiver.call_args_list[1].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
"library_container": LibraryContainerData(
container_key=self.subsection1.container_key,
)
},
)
self.assertDictContainsEntries(
container_update_event_receiver.call_args_list[2].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
"library_container": LibraryContainerData(
container_key=self.subsection2.container_key,
)
},
)
def test_call_container_update_signal_when_update_subsection(self) -> None:
container_update_event_receiver = mock.Mock()
LIBRARY_CONTAINER_UPDATED.connect(container_update_event_receiver)
self._update_container(self.subsection1.container_key, 'New Subsection Display Name')
assert container_update_event_receiver.call_count == 3
self.assertDictContainsEntries(
container_update_event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
"library_container": LibraryContainerData(
container_key=self.subsection1.container_key,
)
},
)
self.assertDictContainsEntries(
container_update_event_receiver.call_args_list[1].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
"library_container": LibraryContainerData(
container_key=self.section1.container_key,
)
},
)
self.assertDictContainsEntries(
container_update_event_receiver.call_args_list[2].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
"library_container": LibraryContainerData(
container_key=self.section2.container_key,
)
},
)
def test_call_container_update_signal_when_update_section(self) -> None:
container_update_event_receiver = mock.Mock()
LIBRARY_CONTAINER_UPDATED.connect(container_update_event_receiver)
self._update_container(self.section1.container_key, 'New Section Display Name')
assert container_update_event_receiver.call_count == 1
self.assertDictContainsEntries(
container_update_event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
"library_container": LibraryContainerData(
container_key=self.section1.container_key,
)
},
)
def test_call_object_changed_signal_when_remove_component(self) -> None:
html_block_1 = self._add_block_to_library(
self.lib1.library_key, "html", "html3",
@@ -941,7 +1042,8 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
)
assert event_reciver.call_count == 1
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_reciver.call_args_list[0].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -950,7 +1052,6 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
changes=["units"],
),
},
event_reciver.call_args_list[0].kwargs,
)
def test_call_object_changed_signal_when_remove_unit(self) -> None:
@@ -973,7 +1074,8 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
)
assert event_reciver.call_count == 1
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_reciver.call_args_list[0].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -982,7 +1084,6 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
changes=["subsections"],
),
},
event_reciver.call_args_list[0].kwargs,
)
def test_call_object_changed_signal_when_remove_subsection(self) -> None:
@@ -1011,7 +1112,8 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
)
assert event_reciver.call_count == 1
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_reciver.call_args_list[0].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -1020,7 +1122,6 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
changes=["sections"],
),
},
event_reciver.call_args_list[0].kwargs,
)
def test_call_object_changed_signal_when_add_component(self) -> None:
@@ -1044,7 +1145,8 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
)
assert event_reciver.call_count == 2
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_reciver.call_args_list[0].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -1053,9 +1155,9 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
changes=["units"],
),
},
event_reciver.call_args_list[0].kwargs,
)
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_reciver.call_args_list[1].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -1064,7 +1166,6 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
changes=["units"],
),
},
event_reciver.call_args_list[1].kwargs,
)
def test_call_object_changed_signal_when_add_unit(self) -> None:
@@ -1081,7 +1182,8 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
entities_action=authoring_api.ChildrenEntitiesAction.APPEND,
)
assert event_reciver.call_count == 2
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_reciver.call_args_list[0].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -1090,9 +1192,9 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
changes=["subsections"],
),
},
event_reciver.call_args_list[0].kwargs,
)
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_reciver.call_args_list[1].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -1101,7 +1203,6 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
changes=["subsections"],
),
},
event_reciver.call_args_list[1].kwargs,
)
def test_call_object_changed_signal_when_add_subsection(self) -> None:
@@ -1129,7 +1230,8 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
entities_action=authoring_api.ChildrenEntitiesAction.APPEND,
)
assert event_reciver.call_count == 2
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_reciver.call_args_list[0].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -1138,9 +1240,9 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
changes=["sections"],
),
},
event_reciver.call_args_list[0].kwargs,
)
self.assertDictContainsSubset(
self.assertDictContainsEntries(
event_reciver.call_args_list[1].kwargs,
{
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
@@ -1149,7 +1251,6 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
changes=["sections"],
),
},
event_reciver.call_args_list[1].kwargs,
)
def test_delete_component_and_revert(self) -> None:
@@ -1172,8 +1273,13 @@ class ContentLibraryContainersTest(ContentLibrariesRestApiTest):
api.revert_changes(self.lib1.library_key)
assert container_event_receiver.call_count == 1
assert {
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
"library_container": LibraryContainerData(container_key=self.unit3.container_key),
}.items() <= container_event_receiver.call_args_list[0].kwargs.items()
self.assertDictContainsEntries(
container_event_receiver.call_args_list[0].kwargs,
{
"signal": LIBRARY_CONTAINER_UPDATED,
"sender": None,
"library_container": LibraryContainerData(
container_key=self.unit3.container_key
),
},
)