Skip to content

Commit d3537e4

Browse files
feat: LP updated event
Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7b62c3e commit d3537e4

3 files changed

Lines changed: 93 additions & 0 deletions

File tree

src/openedx_content/applets/publishing/api.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,21 @@ def update_learning_package(
157157
lp.updated = updated
158158

159159
lp.save()
160+
161+
# Emit LEARNING_PACKAGE_UPDATED once the transaction commits. Note: we only
162+
# reach this point if at least one of key/title/description/updated was
163+
# passed in (the early-return above handles the no-op case), so the update
164+
# really did touch the row.
165+
lp_id = lp.id
166+
lp_title = lp.title
167+
168+
def send_event():
169+
signals.LEARNING_PACKAGE_UPDATED.send_event(
170+
learning_package=signals.LearningPackageEventData(id=lp_id, title=lp_title),
171+
)
172+
173+
on_commit(send_event)
174+
160175
return lp
161176

162177

src/openedx_content/applets/publishing/signals.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"PublishLogEventData",
1919
# All events:
2020
"LEARNING_PACKAGE_CREATED",
21+
"LEARNING_PACKAGE_UPDATED",
2122
"LEARNING_PACKAGE_DELETED",
2223
"LEARNING_PACKAGE_ENTITIES_CHANGED",
2324
"LEARNING_PACKAGE_ENTITIES_PUBLISHED",
@@ -106,6 +107,35 @@ class PublishLogEventData:
106107
"""
107108

108109

110+
LEARNING_PACKAGE_UPDATED = OpenEdxPublicSignal(
111+
event_type="org.openedx.content.publishing.lp_updated.v1",
112+
data={
113+
"learning_package": LearningPackageEventData,
114+
},
115+
)
116+
"""
117+
A ``LearningPackage``'s own metadata (key, title, and/or description) has been
118+
changed.
119+
120+
This is emitted only when the ``update_learning_package`` API is called, with at
121+
least one field change that actually modifies the row.
122+
123+
This event covers changes to the ``LearningPackage`` row itself (its ``key``,
124+
``title``, and ``description``). Changes to the content inside the package
125+
(entities, versions, drafts, publishes) are covered by
126+
``LEARNING_PACKAGE_ENTITIES_CHANGED`` and ``LEARNING_PACKAGE_ENTITIES_PUBLISHED``
127+
instead.
128+
129+
The ``learning_package`` payload reflects the ``id`` and the post-update
130+
``title`` of the package.
131+
132+
💾 This event is only emitted after the enclosing database transaction has
133+
been committed. If the transaction is rolled back, no event is emitted.
134+
135+
⏳ This event is emitted synchronously.
136+
"""
137+
138+
109139
LEARNING_PACKAGE_DELETED = OpenEdxPublicSignal(
110140
event_type="org.openedx.content.publishing.lp_deleted.v1",
111141
data={

tests/openedx_content/applets/publishing/test_signals.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,54 @@ def test_learning_package_created_aborted() -> None:
7373
api.create_learning_package(key="lp1", title="Test LP 📦")
7474

7575

76+
# LEARNING_PACKAGE_UPDATED
77+
78+
79+
def test_learning_package_updated() -> None:
80+
"""
81+
Test that LEARNING_PACKAGE_UPDATED is emitted when
82+
``update_learning_package`` actually changes a field, and that the payload
83+
reflects the post-update title.
84+
"""
85+
learning_package = api.create_learning_package(key="lp1", title="Original Title")
86+
87+
with capture_events(signals=[api.signals.LEARNING_PACKAGE_UPDATED], expected_count=1) as captured:
88+
api.update_learning_package(learning_package.id, title="New Title 📦")
89+
90+
event = captured[0]
91+
assert event.signal is api.signals.LEARNING_PACKAGE_UPDATED
92+
assert event.kwargs["learning_package"].id == learning_package.id
93+
assert event.kwargs["learning_package"].title == "New Title 📦"
94+
95+
96+
def test_learning_package_updated_noop() -> None:
97+
"""
98+
Test that LEARNING_PACKAGE_UPDATED is NOT emitted when
99+
``update_learning_package`` is called with no field changes (the early
100+
return in the API means the row is never saved).
101+
"""
102+
learning_package = api.create_learning_package(key="lp1", title="Test LP 📦")
103+
104+
with capture_events(signals=[api.signals.LEARNING_PACKAGE_UPDATED], expected_count=0):
105+
api.update_learning_package(learning_package.id)
106+
107+
108+
def test_learning_package_updated_aborted() -> None:
109+
"""
110+
Test that LEARNING_PACKAGE_UPDATED is NOT emitted when the transaction
111+
that would have updated the ``LearningPackage`` is rolled back.
112+
"""
113+
learning_package = api.create_learning_package(key="lp1", title="Original Title")
114+
115+
with capture_events(signals=[api.signals.LEARNING_PACKAGE_UPDATED], expected_count=0):
116+
with abort_transaction():
117+
api.update_learning_package(learning_package.id, title="Not going to stick")
118+
119+
# Confirm the title was not actually changed:
120+
learning_package.refresh_from_db()
121+
assert learning_package.title == "Original Title"
122+
123+
76124
# LEARNING_PACKAGE_DELETED
77125

78126

0 commit comments

Comments
 (0)