Skip to content

Commit ebda514

Browse files
committed
fix(user-sync): prune a single-valued key stored with an empty value
- a stored [] on employment_contract_status is now removed by --prune
1 parent 9b36e4e commit ebda514

4 files changed

Lines changed: 53 additions & 5 deletions

File tree

‎src/osw/tools/user_sync/prune.py‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,29 @@
1717

1818
from osw.wtsite import WtPage
1919

20+
_EMPTY = (None, [], "", {})
2021

21-
def _is_multi_valued(key: str) -> bool:
22+
23+
def is_multi_valued(key: str) -> bool:
2224
"""True if the User field is a list or a set, False for any other key."""
2325
model_field = User.__fields__.get(key)
2426
return model_field is not None and model_field.shape != SHAPE_SINGLETON
2527

2628

29+
def needs_clearing(key: str, value: Any) -> bool:
30+
"""True if a present stored value of ``key`` must be cleared by pruning.
31+
32+
A list or set field needs it only when it is not empty. Any other key needs
33+
it whenever it is present, also with an empty value such as ``[]``, which is
34+
not a valid value for a single-valued field.
35+
"""
36+
if is_multi_valued(key):
37+
return value not in _EMPTY
38+
return True
39+
40+
2741
def clear_jsondata_keys(osw: Any, title: str, keys: Iterable[str]) -> bool:
28-
"""Clear each present, non-empty key in the page's jsondata slot.
42+
"""Clear each key in the page's jsondata slot that needs_clearing selects.
2943
3044
A list or set field becomes ``[]``. Any other key is deleted. The page is
3145
read with a new WtPage, which downloads the current revision and bypasses the
@@ -40,8 +54,8 @@ def clear_jsondata_keys(osw: Any, title: str, keys: Iterable[str]) -> bool:
4054
return False
4155
changed = False
4256
for key in keys:
43-
if key in content and content[key] not in (None, [], "", {}):
44-
if _is_multi_valued(key):
57+
if key in content and needs_clearing(key, content[key]):
58+
if is_multi_valued(key):
4559
content[key] = []
4660
else:
4761
del content[key]

‎src/osw/tools/user_sync/reconcile.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .config import PROTECTED_FIELDS
1818
from .existing import ExistingUsers
1919
from .mapping import ProposedUser
20+
from .prune import needs_clearing
2021

2122
NEW = "new"
2223
GAP_FILL = "gap_fill"
@@ -100,7 +101,9 @@ def _has_protected_value(
100101
"""
101102
if stored_jsondata is None:
102103
return _existing_has_iri(entity, name)
103-
return not _is_empty(stored_jsondata.get(name))
104+
if name not in stored_jsondata:
105+
return False
106+
return needs_clearing(name, stored_jsondata[name])
104107

105108

106109
def existing_fields(entity: Any) -> Dict[str, Any]:

‎tests/tools/test_user_sync_reconcile.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,18 @@ def test_placeholder_user_without_username_still_gets_it():
270270
)
271271
assert change.category == GAP_FILL
272272
assert [d.name for d in change.diffs] == ["username"]
273+
274+
275+
def test_protected_field_stored_as_empty_list_is_removed_when_pruning():
276+
# [] is not a valid value for the single-valued field, so it is removed.
277+
entity = _entity()
278+
entity.__iris__ = {"employment_contract_status": "Item:OSWdefault"}
279+
change = reconcile_user(
280+
_proposed(),
281+
entity,
282+
prune=True,
283+
stored_jsondata={"username": ORCID, "employment_contract_status": []},
284+
)
285+
assert [(d.name, d.status) for d in change.diffs] == [
286+
("employment_contract_status", REMOVE)
287+
]

‎tests/tools/test_user_sync_store.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,19 @@ def test_clear_jsondata_keys_does_not_add_absent_keys_or_write(monkeypatch):
365365
assert written is False
366366
assert page.content == {"website": [], "username": "Alice"}
367367
assert page.comments == []
368+
369+
370+
def test_clear_jsondata_keys_deletes_an_empty_single_valued_key(monkeypatch):
371+
page = FakePrunePage({
372+
"website": [],
373+
"employment_contract_status": [],
374+
"username": "Alice",
375+
})
376+
_patch_page(monkeypatch, page)
377+
written = clear_jsondata_keys(
378+
SimpleNamespace(site=None),
379+
ITEM_TITLE,
380+
["website", "employment_contract_status"],
381+
)
382+
assert written is True
383+
assert page.content == {"website": [], "username": "Alice"}

0 commit comments

Comments
 (0)