Skip to content

Commit f273daa

Browse files
committed
simplify search by guid in inveniordm writer
1 parent 154db32 commit f273daa

5 files changed

Lines changed: 103 additions & 9 deletions

File tree

commonmeta/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"""
1111

1212
__title__ = "commonmeta-py"
13-
__version__ = "0.221"
13+
__version__ = "0.222"
1414
__author__ = "Martin Fenner"
1515
__license__ = "MIT"
1616

commonmeta/readers/inveniordm_reader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ def search_by_guid(guid, host, token) -> str | None:
478478
"Authorization": f"Bearer {token}",
479479
"Content-Type": "application/json",
480480
}
481-
params = {"q": f'guid:"{guid}"', "size": 1}
481+
params = {"q": f'metadata.identifiers.identifier:"{guid}"', "size": 1}
482482
try:
483483
response = http.get(
484484
f"https://{host}/api/records", headers=headers, params=params

commonmeta/writers/inveniordm_writer.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -638,18 +638,18 @@ def upsert_record(
638638
# Check if record already exists in InvenioRDM
639639
record["id"] = search_by_doi(doi_from_url(record.get("doi")), host, token)
640640

641-
# Else check if record guid exists in InvenioRDM
641+
# Also check by record guid
642642
if record["id"] is None:
643643
guid = next(
644644
(
645-
normalize_url(identifier.get("identifier"))
646-
for identifier in wrap(dig(output, "metadata.identifiers"))
647-
if identifier.get("scheme") == "guid"
648-
and identifier.get("identifier", None) is not None
645+
i.get("identifier")
646+
for i in wrap(metadata.identifiers)
647+
if i.get("identifierType") == "GUID" and i.get("identifier")
649648
),
650649
None,
651650
)
652-
record["id"] = search_by_guid(guid, host, token)
651+
if guid is not None:
652+
record["id"] = search_by_guid(guid, host, token)
653653

654654
if record["previous_doi"] is not None:
655655
record["previous_id"] = search_by_doi(

tests/test-inveniordm_reader.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
"""InvenioRDM reader tests"""
33

44
from os import path
5+
from unittest.mock import MagicMock, patch
56

67
import pytest
78

89
from commonmeta import Metadata
10+
from commonmeta.readers.inveniordm_reader import search_by_doi, search_by_guid
911

1012

1113
@pytest.mark.vcr
@@ -592,3 +594,95 @@ def test_subfield_classification():
592594
{"subject": "SSP"},
593595
{"subject": "Stinkin' Publishers"},
594596
]
597+
598+
599+
# --- Unit tests for search_by_guid / search_by_doi ---
600+
601+
602+
def _mock_response(status_code: int, payload: dict) -> MagicMock:
603+
"""Build a minimal mock requests.Response."""
604+
response = MagicMock()
605+
response.status_code = status_code
606+
response.json.return_value = payload
607+
if status_code >= 400:
608+
from requests.exceptions import HTTPError
609+
610+
response.raise_for_status.side_effect = HTTPError(response=response)
611+
else:
612+
response.raise_for_status.return_value = None
613+
return response
614+
615+
616+
def test_search_by_guid_returns_id_when_found():
617+
"""search_by_guid returns the record id from the first hit."""
618+
hit_id = "abc12-def34"
619+
payload = {"hits": {"total": 1, "hits": [{"id": hit_id}]}}
620+
with patch(
621+
"commonmeta.readers.inveniordm_reader.http.get",
622+
return_value=_mock_response(200, payload),
623+
) as mock_get:
624+
result = search_by_guid(
625+
"https://ideophone.org/?p=5639", "rogue-scholar.org", "token"
626+
)
627+
628+
assert result == hit_id
629+
# The query must phrase-quote the GUID so partial URLs don't match
630+
called_params = mock_get.call_args.kwargs["params"]
631+
assert (
632+
called_params["q"]
633+
== 'metadata.identifiers.identifier:"https://ideophone.org/?p=5639"'
634+
)
635+
636+
637+
def test_search_by_guid_returns_none_when_not_found():
638+
"""search_by_guid returns None when no record matches the GUID."""
639+
payload = {"hits": {"total": 0, "hits": []}}
640+
with patch(
641+
"commonmeta.readers.inveniordm_reader.http.get",
642+
return_value=_mock_response(200, payload),
643+
):
644+
result = search_by_guid(
645+
"https://ideophone.org/?p=9999", "rogue-scholar.org", "token"
646+
)
647+
648+
assert result is None
649+
650+
651+
def test_search_by_guid_returns_none_on_rate_limit():
652+
"""search_by_guid returns None when the API responds with 429."""
653+
with patch(
654+
"commonmeta.readers.inveniordm_reader.http.get",
655+
return_value=_mock_response(429, {}),
656+
):
657+
result = search_by_guid(
658+
"https://ideophone.org/?p=5639", "rogue-scholar.org", "token"
659+
)
660+
661+
assert result is None
662+
663+
664+
def test_search_by_doi_returns_id_when_found():
665+
"""search_by_doi returns the record id from the first hit."""
666+
hit_id = "xyz98-uvw76"
667+
payload = {"hits": {"total": 1, "hits": [{"id": hit_id}]}}
668+
with patch(
669+
"commonmeta.readers.inveniordm_reader.http.get",
670+
return_value=_mock_response(200, payload),
671+
) as mock_get:
672+
result = search_by_doi("10.59350/dn2mm-m9q51", "rogue-scholar.org", "token")
673+
674+
assert result == hit_id
675+
called_params = mock_get.call_args.kwargs["params"]
676+
assert called_params["q"] == "doi:10.59350/dn2mm-m9q51"
677+
678+
679+
def test_search_by_doi_returns_none_when_not_found():
680+
"""search_by_doi returns None when no record matches the DOI."""
681+
payload = {"hits": {"total": 0, "hits": []}}
682+
with patch(
683+
"commonmeta.readers.inveniordm_reader.http.get",
684+
return_value=_mock_response(200, payload),
685+
):
686+
result = search_by_doi("10.59350/nonexistent", "rogue-scholar.org", "token")
687+
688+
assert result is None

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)