fix: Only update downstream_customized for upstream-linked blocks (#37412)

We only need to track field customizations for upstream-linked (i.e.,
library-linked) blocks. Thd downstream_customized field is irrelevant for other
blocks. It would just add a ton of noise to the OLX.

Additionally, we now clear downstream_customized when severing an upstream
link.

Fixes: https://github.com/openedx/edx-platform/issues/37411
This commit is contained in:
Kyle McCormick
2025-10-06 11:33:25 -04:00
committed by GitHub
parent 9ee56dee85
commit 8b6a94bc8d
2 changed files with 20 additions and 4 deletions

View File

@@ -533,15 +533,19 @@ class UpstreamTestCase(ModuleStoreTestCase):
"""
Does sever_upstream_link correctly disconnect a block from its upstream?
"""
# Start with a course block that is linked+synced to a content library block.
# Start with a course block that is linked+synced to a content library block
# and has a customizred title.
downstream = BlockFactory.create(category='html', parent=self.unit, upstream=str(self.upstream_key))
sync_from_upstream_block(downstream, self.user)
downstream.display_name = "Downstream Title"
save_xblock_with_callback(downstream, self.user)
# (sanity checks)
assert downstream.upstream == str(self.upstream_key)
assert downstream.upstream_version == 2
assert downstream.upstream_display_name == "Upstream Title V2"
assert downstream.display_name == "Upstream Title V2"
assert downstream.display_name == "Downstream Title"
assert downstream.downstream_customized == ["display_name"]
assert downstream.data == "<html><body>Upstream content V2</body></html>"
assert downstream.copied_from_block is None
@@ -552,14 +556,21 @@ class UpstreamTestCase(ModuleStoreTestCase):
assert downstream.upstream is None
assert downstream.upstream_version is None
assert downstream.upstream_display_name is None
assert downstream.downstream_customized == []
# BUT, the content which was synced into the upstream remains.
assert downstream.display_name == "Upstream Title V2"
# BUT, the content remains.
assert downstream.display_name == "Downstream Title"
assert downstream.data == "<html><body>Upstream content V2</body></html>"
# AND, we have recorded the old upstream as our copied_from_block.
assert downstream.copied_from_block == str(self.upstream_key)
# Finally... unlike an upstream-linked block, our unlinked block should not
# have its downstream_customized updated when the title changes.
downstream.display_name = "Downstream Title II"
save_xblock_with_callback(downstream, self.user)
assert downstream.downstream_customized == []
def test_sync_library_block_tags(self):
upstream_lib_block_key = libs.create_library_block(self.library.key, "html", "upstream").usage_key
upstream_lib_block = xblock.load_block(upstream_lib_block_key, self.user)

View File

@@ -386,6 +386,7 @@ def sever_upstream_link(downstream: XBlock) -> list[XBlock]:
downstream.copied_from_block = downstream.upstream
downstream.upstream = None
downstream.upstream_version = None
downstream.downstream_customized = []
for _, fetched_upstream_field in downstream.get_customizable_fields().items():
# Downstream-only fields don't have an upstream fetch field
if fetched_upstream_field is None:
@@ -527,6 +528,10 @@ class UpstreamSyncMixin(XBlockMixin):
Update `downstream_customized` when a customizable field is modified.
"""
super().editor_saved(user, old_metadata, old_content)
if not self.upstream:
# If a block does not have an upstream, then we do not need to track its
# customizations.
return
customizable_fields = self.get_customizable_fields()
new_data = (
self.get_explicitly_set_fields_by_scope(Scope.settings)