Skip to content

Commit

Permalink
feat: emit CONTENT_OBJECT_ASSOCIATIONS_CHANGED
Browse files Browse the repository at this point in the history
whenever a content object's tags or collections have changed,
and handle that event in content/search.

The deprecated CONTENT_OBJECT_TAGS_CHANGED event is still emitted when
tags change; to be removed after Sumac.
  • Loading branch information
pomegranited committed Sep 5, 2024
1 parent 3c6617e commit 0c29552
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 35 deletions.
28 changes: 17 additions & 11 deletions openedx/core/djangoapps/content/search/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@

from django.db.models.signals import post_delete
from django.dispatch import receiver
from openedx_events.content_authoring.data import ContentLibraryData, ContentObjectData, LibraryBlockData, XBlockData
from openedx_events.content_authoring.data import (
ContentLibraryData,
ContentObjectChangedData,
LibraryBlockData,
XBlockData,
)
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import UsageKey
from openedx_events.content_authoring.signals import (
CONTENT_LIBRARY_DELETED,
CONTENT_LIBRARY_UPDATED,
Expand All @@ -16,9 +23,8 @@
XBLOCK_CREATED,
XBLOCK_DELETED,
XBLOCK_UPDATED,
CONTENT_OBJECT_TAGS_CHANGED,
CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
)
from openedx.core.djangoapps.content_tagging.utils import get_content_key_from_string

from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.content.search.models import SearchAccess
Expand Down Expand Up @@ -139,22 +145,22 @@ def content_library_updated_handler(**kwargs) -> None:
update_content_library_index_docs.delay(str(content_library_data.library_key))


@receiver(CONTENT_OBJECT_TAGS_CHANGED)
@receiver(CONTENT_OBJECT_ASSOCIATIONS_CHANGED)
@only_if_meilisearch_enabled
def content_object_tags_changed_handler(**kwargs) -> None:
def content_object_associations_changed_handler(**kwargs) -> None:
"""
Update the tags data in the index for the Content Object
Update the collections/tags data in the index for the Content Object
"""
content_object_tags = kwargs.get("content_object", None)
if not content_object_tags or not isinstance(content_object_tags, ContentObjectData):
content_object = kwargs.get("content_object", None)
if not content_object or not isinstance(content_object, ContentObjectChangedData):
log.error("Received null or incorrect data for event")
return

try:
# Check if valid if course or library block
content_key = get_content_key_from_string(str(content_object_tags.object_id))
except ValueError:
usage_key = UsageKey.from_string(str(content_object.object_id))
except InvalidKeyError:
log.error("Received invalid content object id")
return

upsert_block_tags_index_docs(content_key)
upsert_block_tags_index_docs(usage_key)
13 changes: 8 additions & 5 deletions openedx/core/djangoapps/content_libraries/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@
from opaque_keys import InvalidKeyError
from openedx_events.content_authoring.data import (
ContentLibraryData,
ContentObjectData,
ContentObjectChangedData,
LibraryBlockData,
LibraryCollectionData,
)
from openedx_events.content_authoring.signals import (
CONTENT_OBJECT_TAGS_CHANGED,
CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
CONTENT_LIBRARY_CREATED,
CONTENT_LIBRARY_DELETED,
CONTENT_LIBRARY_UPDATED,
Expand Down Expand Up @@ -1228,10 +1228,13 @@ def update_library_collection_components(
)
)

# Emit a CONTENT_OBJECT_TAGS_CHANGED event for each of the objects added/removed
# Emit a CONTENT_OBJECT_ASSOCIATIONS_CHANGED event for each of the objects added/removed
for usage_key in usage_keys:
CONTENT_OBJECT_TAGS_CHANGED.send_event(
content_object=ContentObjectData(object_id=usage_key),
CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event(
content_object=ContentObjectChangedData(
object_id=str(usage_key),
changes=["collections"],
),
)

return collection
Expand Down
20 changes: 11 additions & 9 deletions openedx/core/djangoapps/content_libraries/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
)
from opaque_keys.edx.locator import LibraryLocatorV2
from openedx_events.content_authoring.data import (
ContentObjectData,
ContentObjectChangedData,
LibraryCollectionData,
)
from openedx_events.content_authoring.signals import (
CONTENT_OBJECT_TAGS_CHANGED,
CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
LIBRARY_COLLECTION_CREATED,
LIBRARY_COLLECTION_UPDATED,
)
Expand Down Expand Up @@ -262,7 +262,7 @@ class ContentLibraryCollectionsTest(ContentLibrariesRestApiTest, OpenEdxEventsTe
Same guidelines as ContentLibrariesTestCase.
"""
ENABLED_OPENEDX_EVENTS = [
CONTENT_OBJECT_TAGS_CHANGED.event_type,
CONTENT_OBJECT_ASSOCIATIONS_CHANGED.event_type,
LIBRARY_COLLECTION_CREATED.event_type,
LIBRARY_COLLECTION_UPDATED.event_type,
]
Expand Down Expand Up @@ -411,10 +411,10 @@ def test_update_library_collection_components(self):

def test_update_library_collection_components_event(self):
"""
Check that a CONTENT_OBJECT_TAGS_CHANGED event is raised for each added/removed component.
Check that a CONTENT_OBJECT_ASSOCIATIONS_CHANGED event is raised for each added/removed component.
"""
event_receiver = mock.Mock()
CONTENT_OBJECT_TAGS_CHANGED.connect(event_receiver)
CONTENT_OBJECT_ASSOCIATIONS_CHANGED.connect(event_receiver)
LIBRARY_COLLECTION_UPDATED.connect(event_receiver)

api.update_library_collection_components(
Expand All @@ -440,20 +440,22 @@ def test_update_library_collection_components_event(self):
)
self.assertDictContainsSubset(
{
"signal": CONTENT_OBJECT_TAGS_CHANGED,
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
"content_object": ContentObjectData(
"content_object": ContentObjectChangedData(
object_id=UsageKey.from_string(self.lib1_problem_block["id"]),
changes=["collections"],
),
},
event_receiver.call_args_list[1].kwargs,
)
self.assertDictContainsSubset(
{
"signal": CONTENT_OBJECT_TAGS_CHANGED,
"signal": CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
"sender": None,
"content_object": ContentObjectData(
"content_object": ContentObjectChangedData(
object_id=UsageKey.from_string(self.lib1_html_block["id"]),
changes=["collections"],
),
},
event_receiver.call_args_list[2].kwargs,
Expand Down
20 changes: 13 additions & 7 deletions openedx/core/djangoapps/content_tagging/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
from openedx_tagging.core.tagging.models.utils import TAGS_CSV_SEPARATOR
from organizations.models import Organization
from .helpers.objecttag_export_helpers import build_object_tree_with_objecttags, iterate_with_level
from openedx_events.content_authoring.data import ContentObjectData
from openedx_events.content_authoring.signals import CONTENT_OBJECT_TAGS_CHANGED
from openedx_events.content_authoring.data import ContentObjectChangedData
from openedx_events.content_authoring.signals import CONTENT_OBJECT_ASSOCIATIONS_CHANGED

from .models import TaxonomyOrg
from .types import ContentKey, TagValuesByObjectIdDict, TagValuesByTaxonomyIdDict, TaxonomyDict
Expand Down Expand Up @@ -301,9 +301,12 @@ def set_exported_object_tags(
create_invalid=True,
taxonomy_export_id=str(taxonomy_export_id),
)
CONTENT_OBJECT_TAGS_CHANGED.send_event(
CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event(
time=now(),
content_object=ContentObjectData(object_id=content_key_str)
content_object=ContentObjectChangedData(
object_id=content_key_str,
changes=["tags"],
)
)


Expand Down Expand Up @@ -378,7 +381,7 @@ def tag_object(
Replaces the existing ObjectTag entries for the given taxonomy + object_id
with the given list of tags, if the taxonomy can be used by the given object_id.
This is a wrapper around oel_tagging.tag_object that adds emitting the `CONTENT_OBJECT_TAGS_CHANGED` event
This is a wrapper around oel_tagging.tag_object that adds emitting the `CONTENT_OBJECT_ASSOCIATIONS_CHANGED` event
when tagging an object.
tags: A list of the values of the tags from this taxonomy to apply.
Expand All @@ -399,9 +402,12 @@ def tag_object(
taxonomy=taxonomy,
tags=tags,
)
CONTENT_OBJECT_TAGS_CHANGED.send_event(
CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event(
time=now(),
content_object=ContentObjectData(object_id=object_id)
content_object=ContentObjectChangedData(
object_id=object_id,
changes=["tags"],
)
)

# Expose the oel_tagging APIs
Expand Down
19 changes: 16 additions & 3 deletions openedx/core/djangoapps/content_tagging/rest_api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from openedx_events.content_authoring.data import ContentObjectData
from openedx_events.content_authoring.signals import CONTENT_OBJECT_TAGS_CHANGED
from openedx_events.content_authoring.data import ContentObjectData, ContentObjectChangedData
from openedx_events.content_authoring.signals import (
CONTENT_OBJECT_ASSOCIATIONS_CHANGED,
CONTENT_OBJECT_TAGS_CHANGED,
)

from ...auth import has_view_object_tags_access
from ...api import (
Expand Down Expand Up @@ -149,14 +152,24 @@ class ObjectTagOrgView(ObjectTagView):

def update(self, request, *args, **kwargs) -> Response:
"""
Extend the update method to fire CONTENT_OBJECT_TAGS_CHANGED event
Extend the update method to fire CONTENT_OBJECT_ASSOCIATIONS_CHANGED event
"""
response = super().update(request, *args, **kwargs)
if response.status_code == 200:
object_id = kwargs.get('object_id')

CONTENT_OBJECT_ASSOCIATIONS_CHANGED.send_event(
content_object=ContentObjectChangedData(
object_id=object_id,
changes=["tags"],
)
)

# Emit a (deprecated) CONTENT_OBJECT_TAGS_CHANGED event too
CONTENT_OBJECT_TAGS_CHANGED.send_event(
content_object=ContentObjectData(object_id=object_id)
)

return response


Expand Down

0 comments on commit 0c29552

Please sign in to comment.