2323 LIBRARY_BLOCK_CREATED ,
2424 LIBRARY_BLOCK_DELETED ,
2525 LIBRARY_BLOCK_UPDATED ,
26+ LIBRARY_BLOCK_PUBLISHED ,
2627 LIBRARY_COLLECTION_CREATED ,
2728 LIBRARY_COLLECTION_DELETED ,
2829 LIBRARY_COLLECTION_UPDATED ,
2930 LIBRARY_CONTAINER_CREATED ,
3031 LIBRARY_CONTAINER_DELETED ,
3132 LIBRARY_CONTAINER_UPDATED ,
33+ LIBRARY_CONTAINER_PUBLISHED ,
3234 XBLOCK_CREATED ,
3335 XBLOCK_DELETED ,
3436 XBLOCK_UPDATED ,
3739
3840from openedx .core .djangoapps .content .course_overviews .models import CourseOverview
3941from openedx .core .djangoapps .content .search .models import SearchAccess
42+ from openedx .core .djangoapps .content_libraries import api as lib_api
4043
4144from .api import (
4245 only_if_meilisearch_enabled ,
@@ -136,6 +139,32 @@ def library_block_updated_handler(**kwargs) -> None:
136139 upsert_library_block_index_doc .apply (args = [str (library_block_data .usage_key )])
137140
138141
142+ @receiver (LIBRARY_BLOCK_PUBLISHED )
143+ @only_if_meilisearch_enabled
144+ def library_block_published_handler (** kwargs ) -> None :
145+ """
146+ Update the index for the content library block when its published version
147+ has changed.
148+ """
149+ library_block_data = kwargs .get ("library_block" , None )
150+ if not library_block_data or not isinstance (library_block_data , LibraryBlockData ): # pragma: no cover
151+ log .error ("Received null or incorrect data for event" )
152+ return
153+
154+ # The PUBLISHED event is sent for any change to the published version including deletes, so check if it exists:
155+ try :
156+ lib_api .get_library_block (library_block_data .usage_key )
157+ except lib_api .ContentLibraryBlockNotFound :
158+ log .info (f"Observed published deletion of library block { str (library_block_data .usage_key )} ." )
159+ # The document should already have been deleted from the search index
160+ # via the DELETED handler, so there's nothing to do now.
161+ return
162+
163+ # Update content library index synchronously to make sure that search index is updated before
164+ # the frontend invalidates/refetches results. This is only a single document update so is very fast.
165+ upsert_library_block_index_doc .apply (args = [str (library_block_data .usage_key )])
166+
167+
139168@receiver (LIBRARY_BLOCK_DELETED )
140169@only_if_meilisearch_enabled
141170def library_block_deleted (** kwargs ) -> None :
@@ -162,14 +191,14 @@ def content_library_updated_handler(**kwargs) -> None:
162191 if not content_library_data or not isinstance (content_library_data , ContentLibraryData ): # pragma: no cover
163192 log .error ("Received null or incorrect data for event" )
164193 return
194+ library_key = content_library_data .library_key
165195
166- # Update content library index synchronously to make sure that search index is updated before
167- # the frontend invalidates/refetches index.
168- # Currently, this is only required to make sure that removed/discarded components are removed
169- # from the search index and displayed to user properly. If it becomes a performance bottleneck
170- # for other update operations other than discard, we can update CONTENT_LIBRARY_UPDATED event
171- # to include a parameter which can help us decide if the task needs to run sync or async.
172- update_content_library_index_docs .apply (args = [str (content_library_data .library_key )])
196+ # For now we assume the library has been renamed. Few other things will trigger this event.
197+
198+ # Update ALL items in the library, because their breadcrumbs will be outdated.
199+ # TODO: just patch the "breadcrumbs" field? It's the same on every one.
200+ # TODO: check if the library display_name has actually changed before updating all items?
201+ update_content_library_index_docs .apply (args = [str (library_key )])
173202
174203
175204@receiver (LIBRARY_COLLECTION_CREATED )
@@ -248,17 +277,34 @@ def library_container_updated_handler(**kwargs) -> None:
248277 log .error ("Received null or incorrect data for event" )
249278 return
250279
251- if library_container .background :
252- update_library_container_index_doc .delay (
253- str (library_container .container_key ),
254- )
255- else :
256- # Update container index synchronously to make sure that search index is updated before
257- # the frontend invalidates/refetches index.
258- # See content_library_updated_handler for more details.
259- update_library_container_index_doc .apply (args = [
260- str (library_container .container_key ),
261- ])
280+ update_library_container_index_doc .apply (args = [
281+ str (library_container .container_key ),
282+ ])
283+
284+
285+ @receiver (LIBRARY_CONTAINER_PUBLISHED )
286+ @only_if_meilisearch_enabled
287+ def library_container_published_handler (** kwargs ) -> None :
288+ """
289+ Update the index for the content library container when its published
290+ version has changed.
291+ """
292+ library_container = kwargs .get ("library_container" , None )
293+ if not library_container or not isinstance (library_container , LibraryContainerData ): # pragma: no cover
294+ log .error ("Received null or incorrect data for event" )
295+ return
296+ # The PUBLISHED event is sent for any change to the published version including deletes, so check if it exists:
297+ try :
298+ lib_api .get_container (library_container .container_key )
299+ except lib_api .ContentLibraryContainerNotFound :
300+ log .info (f"Observed published deletion of container { str (library_container .container_key )} ." )
301+ # The document should already have been deleted from the search index
302+ # via the DELETED handler, so there's nothing to do now.
303+ return
304+
305+ update_library_container_index_doc .apply (args = [
306+ str (library_container .container_key ),
307+ ])
262308
263309
264310@receiver (LIBRARY_CONTAINER_DELETED )
@@ -275,3 +321,6 @@ def library_container_deleted(**kwargs) -> None:
275321 # Update content library index synchronously to make sure that search index is updated before
276322 # the frontend invalidates/refetches results. This is only a single document update so is very fast.
277323 delete_library_container_index_doc .apply (args = [str (library_container .container_key )])
324+ # TODO: post-Teak, move all the celery tasks directly inline into this handlers? Because now the
325+ # events are emitted in an [async] worker, so it doesn't matter if the handlers are synchronous.
326+ # See https://github.com/openedx/edx-platform/pull/36640 discussion.
0 commit comments