fix: updating search index on container delete and restore (#36956)
Update children and parents index data on container delete and restore.
This commit is contained in:
@@ -237,6 +237,11 @@ class TestSearchApi(ModuleStoreTestCase):
|
||||
title="Subsection 1",
|
||||
user_id=None,
|
||||
)
|
||||
library_api.update_container_children(
|
||||
self.subsection.container_key,
|
||||
[self.unit.container_key],
|
||||
None,
|
||||
)
|
||||
self.subsection_key = "lct:org1:lib:subsection:subsection-1"
|
||||
self.section = library_api.create_container(
|
||||
self.library.key,
|
||||
@@ -246,6 +251,11 @@ class TestSearchApi(ModuleStoreTestCase):
|
||||
user_id=None,
|
||||
)
|
||||
self.section_key = "lct:org1:lib:section:section-1"
|
||||
library_api.update_container_children(
|
||||
self.section.container_key,
|
||||
[self.subsection.container_key],
|
||||
None,
|
||||
)
|
||||
|
||||
self.unit_dict = {
|
||||
"id": "lctorg1libunitunit-1-e4527f7c",
|
||||
@@ -278,10 +288,10 @@ class TestSearchApi(ModuleStoreTestCase):
|
||||
"type": "library_container",
|
||||
"display_name": "Subsection 1",
|
||||
# description is not set for containers
|
||||
"num_children": 0,
|
||||
"num_children": 1,
|
||||
"content": {
|
||||
"child_usage_keys": [],
|
||||
"child_display_names": [],
|
||||
"child_usage_keys": ["lct:org1:lib:unit:unit-1"],
|
||||
"child_display_names": ["Unit 1"],
|
||||
},
|
||||
"publish_status": "never",
|
||||
"context_key": "lib:org1:lib",
|
||||
@@ -301,10 +311,10 @@ class TestSearchApi(ModuleStoreTestCase):
|
||||
"type": "library_container",
|
||||
"display_name": "Section 1",
|
||||
# description is not set for containers
|
||||
"num_children": 0,
|
||||
"num_children": 1,
|
||||
"content": {
|
||||
"child_usage_keys": [],
|
||||
"child_display_names": [],
|
||||
"child_usage_keys": ["lct:org1:lib:subsection:subsection-1"],
|
||||
"child_display_names": ["Subsection 1"],
|
||||
},
|
||||
"publish_status": "never",
|
||||
"context_key": "lib:org1:lib",
|
||||
@@ -345,11 +355,11 @@ class TestSearchApi(ModuleStoreTestCase):
|
||||
doc_unit = copy.deepcopy(self.unit_dict)
|
||||
doc_unit["tags"] = {}
|
||||
doc_unit["collections"] = {'display_name': [], 'key': []}
|
||||
doc_unit["subsections"] = {"display_name": [], "key": []}
|
||||
doc_unit["subsections"] = {'display_name': ['Subsection 1'], 'key': ['lct:org1:lib:subsection:subsection-1']}
|
||||
doc_subsection = copy.deepcopy(self.subsection_dict)
|
||||
doc_subsection["tags"] = {}
|
||||
doc_subsection["collections"] = {'display_name': [], 'key': []}
|
||||
doc_subsection["sections"] = {'display_name': [], 'key': []}
|
||||
doc_subsection["sections"] = {'display_name': ['Section 1'], 'key': ['lct:org1:lib:section:section-1']}
|
||||
doc_section = copy.deepcopy(self.section_dict)
|
||||
doc_section["tags"] = {}
|
||||
doc_section["collections"] = {'display_name': [], 'key': []}
|
||||
@@ -387,11 +397,11 @@ class TestSearchApi(ModuleStoreTestCase):
|
||||
doc_unit = copy.deepcopy(self.unit_dict)
|
||||
doc_unit["tags"] = {}
|
||||
doc_unit["collections"] = {"display_name": [], "key": []}
|
||||
doc_unit["subsections"] = {"display_name": [], "key": []}
|
||||
doc_unit["subsections"] = {'display_name': ['Subsection 1'], 'key': ['lct:org1:lib:subsection:subsection-1']}
|
||||
doc_subsection = copy.deepcopy(self.subsection_dict)
|
||||
doc_subsection["tags"] = {}
|
||||
doc_subsection["collections"] = {'display_name': [], 'key': []}
|
||||
doc_subsection["sections"] = {'display_name': [], 'key': []}
|
||||
doc_subsection["sections"] = {'display_name': ['Section 1'], 'key': ['lct:org1:lib:section:section-1']}
|
||||
doc_section = copy.deepcopy(self.section_dict)
|
||||
doc_section["tags"] = {}
|
||||
doc_section["collections"] = {'display_name': [], 'key': []}
|
||||
@@ -989,12 +999,70 @@ class TestSearchApi(ModuleStoreTestCase):
|
||||
"""
|
||||
container = getattr(self, container_type)
|
||||
container_dict = getattr(self, f"{container_type}_dict")
|
||||
update_doc_calls = []
|
||||
|
||||
def clear_contents(data: dict):
|
||||
return {
|
||||
**data,
|
||||
"num_children": 0,
|
||||
"content": {
|
||||
"child_usage_keys": [],
|
||||
"child_display_names": [],
|
||||
},
|
||||
}
|
||||
if container_type == "unit":
|
||||
update_doc_calls.append(call([clear_contents(self.subsection_dict)]))
|
||||
elif container_type == "subsection":
|
||||
update_doc_calls.append(call([clear_contents(self.section_dict)]))
|
||||
update_doc_calls.append(call([{
|
||||
'id': self.unit_dict['id'],
|
||||
'subsections': {'display_name': [], 'key': []},
|
||||
}]))
|
||||
elif container_type == "section":
|
||||
update_doc_calls.append(call([{
|
||||
'id': self.subsection_dict['id'],
|
||||
'sections': {'display_name': [], 'key': []},
|
||||
}]))
|
||||
|
||||
library_api.delete_container(container.container_key)
|
||||
|
||||
mock_meilisearch.return_value.index.return_value.delete_document.assert_called_once_with(
|
||||
container_dict["id"],
|
||||
)
|
||||
# Parent containers index data is updated.
|
||||
if update_doc_calls:
|
||||
mock_meilisearch.return_value.index.return_value.update_documents.assert_has_calls(
|
||||
update_doc_calls,
|
||||
any_order=True,
|
||||
)
|
||||
|
||||
# Restore
|
||||
library_api.restore_container(container.container_key)
|
||||
if container_type == "unit":
|
||||
update_doc_calls.append(call([self.subsection_dict]))
|
||||
elif container_type == "subsection":
|
||||
update_doc_calls.append(call([self.section_dict]))
|
||||
update_doc_calls.append(call([{
|
||||
'id': self.unit_dict['id'],
|
||||
'subsections': {
|
||||
'display_name': [self.subsection_dict['display_name']],
|
||||
'key': [self.subsection_key],
|
||||
},
|
||||
}]))
|
||||
elif container_type == "section":
|
||||
update_doc_calls.append(call([{
|
||||
'id': self.subsection_dict['id'],
|
||||
'sections': {
|
||||
'display_name': [self.section_dict['display_name']],
|
||||
'key': [self.section_key],
|
||||
},
|
||||
}]))
|
||||
# Parent containers index data is updated on restore again.
|
||||
if update_doc_calls:
|
||||
mock_meilisearch.return_value.index.return_value.update_documents.assert_has_calls(
|
||||
update_doc_calls,
|
||||
any_order=True,
|
||||
)
|
||||
|
||||
@ddt.data(
|
||||
"unit",
|
||||
|
||||
@@ -308,6 +308,12 @@ def update_container(
|
||||
|
||||
version: ContainerVersion
|
||||
affected_containers: list[ContainerMetadata] = []
|
||||
# Get children containers or components to update their index data
|
||||
children = get_container_children(
|
||||
container_key,
|
||||
published=False,
|
||||
)
|
||||
child_key_name = 'container_key'
|
||||
|
||||
match container_type:
|
||||
case ContainerType.Unit:
|
||||
@@ -318,6 +324,8 @@ def update_container(
|
||||
created_by=user_id,
|
||||
)
|
||||
affected_containers = get_containers_contains_item(container_key)
|
||||
# Components have usage_key instead of container_key
|
||||
child_key_name = 'usage_key'
|
||||
case ContainerType.Subsection:
|
||||
version = authoring_api.create_next_subsection_version(
|
||||
container.subsection,
|
||||
@@ -354,6 +362,16 @@ def update_container(
|
||||
container_key=affected_container.container_key,
|
||||
)
|
||||
)
|
||||
# Update children components and containers index data, for example,
|
||||
# All subsections under a section have section key in index that needs to be updated.
|
||||
# So if parent section name has been changed, it needs to be reflected in sections key of children
|
||||
for child in children:
|
||||
CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event(
|
||||
content_object=ContentObjectChangedData(
|
||||
object_id=str(getattr(child, child_key_name)),
|
||||
changes=[container_key.container_type + "s"],
|
||||
),
|
||||
)
|
||||
|
||||
return ContainerMetadata.from_container(library_key, version.container)
|
||||
|
||||
@@ -369,11 +387,17 @@ def delete_container(
|
||||
library_key = container_key.lib_key
|
||||
container = _get_container_from_key(container_key)
|
||||
|
||||
affected_containers = get_containers_contains_item(container_key)
|
||||
# Fetch related collections and containers before soft-delete
|
||||
affected_collections = authoring_api.get_entity_collections(
|
||||
container.publishable_entity.learning_package_id,
|
||||
container.key,
|
||||
)
|
||||
affected_containers = get_containers_contains_item(container_key)
|
||||
# Get children containers or components to update their index data
|
||||
children = get_container_children(
|
||||
container_key,
|
||||
published=False,
|
||||
)
|
||||
authoring_api.soft_delete_draft(container.pk)
|
||||
|
||||
LIBRARY_CONTAINER_DELETED.send_event(
|
||||
@@ -404,6 +428,21 @@ def delete_container(
|
||||
container_key=affected_container.container_key,
|
||||
)
|
||||
)
|
||||
container_type = ContainerType(container_key.container_type)
|
||||
key_name = 'container_key'
|
||||
if container_type == ContainerType.Unit:
|
||||
# Components have usage_key instead of container_key
|
||||
key_name = 'usage_key'
|
||||
# Update children components and containers index data, for example,
|
||||
# All subsections under a section have section key in index that needs to be updated.
|
||||
# So if parent section is deleted, it needs to be removed from sections key of children
|
||||
for child in children:
|
||||
CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event(
|
||||
content_object=ContentObjectChangedData(
|
||||
object_id=str(getattr(child, key_name)),
|
||||
changes=[container_key.container_type + "s"],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def restore_container(container_key: LibraryContainerLocator) -> None:
|
||||
@@ -419,6 +458,13 @@ def restore_container(container_key: LibraryContainerLocator) -> None:
|
||||
)
|
||||
|
||||
authoring_api.set_draft_version(container.pk, container.versioning.latest.pk)
|
||||
# Fetch related containers after restore
|
||||
affected_containers = get_containers_contains_item(container_key)
|
||||
# Get children containers or components to update their index data
|
||||
children = get_container_children(
|
||||
container_key,
|
||||
published=False,
|
||||
)
|
||||
|
||||
LIBRARY_CONTAINER_CREATED.send_event(
|
||||
library_container=LibraryContainerData(
|
||||
@@ -426,11 +472,15 @@ def restore_container(container_key: LibraryContainerLocator) -> None:
|
||||
)
|
||||
)
|
||||
|
||||
# Add tags and collections back to index
|
||||
content_changes = ["collections", "tags"]
|
||||
if affected_containers and len(affected_containers) > 0:
|
||||
# Update parent key data in index. Eg. `sections` key in index for subsection
|
||||
content_changes.append(str(affected_containers[0].container_type.value) + "s")
|
||||
# Add tags, collections and parent data back to index
|
||||
CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event(
|
||||
content_object=ContentObjectChangedData(
|
||||
object_id=str(container_key),
|
||||
changes=["collections", "tags"],
|
||||
changes=content_changes,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -447,6 +497,28 @@ def restore_container(container_key: LibraryContainerLocator) -> None:
|
||||
),
|
||||
)
|
||||
)
|
||||
# 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,
|
||||
)
|
||||
)
|
||||
container_type = ContainerType(container_key.container_type)
|
||||
key_name = 'container_key'
|
||||
if container_type == ContainerType.Unit:
|
||||
key_name = 'usage_key'
|
||||
# Update children components and containers index data, for example,
|
||||
# All subsections under a section have section key in index that needs to be updated.
|
||||
# Should restore removed parent section in sections key of children subsections
|
||||
for child in children:
|
||||
CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event(
|
||||
content_object=ContentObjectChangedData(
|
||||
object_id=str(getattr(child, key_name)),
|
||||
changes=[container_key.container_type + "s"],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def get_container_children(
|
||||
|
||||
Reference in New Issue
Block a user