Fix Google Books search cache failures (#958)

Recognises google error 503s as invalid results and does not add to
results cache.

Fixes #945
This commit is contained in:
Alex
2026-05-08 11:17:57 +01:00
committed by GitHub
parent b10a5a35ca
commit 4c782ca92d
2 changed files with 72 additions and 10 deletions
+19 -10
View File
@@ -47,6 +47,11 @@ _HTTP_STATUS_NOT_FOUND = HTTPStatus.NOT_FOUND
GOOGLE_BOOKS_BASE_URL = "https://www.googleapis.com/books/v1"
class _GoogleBooksRequestError(Exception):
"""Raised when Google Books does not return a usable API response."""
# Sort mapping - Google only supports "relevance" and "newest"
SORT_MAPPING: dict[SortOrder, str | None] = {
SortOrder.RELEVANCE: None, # Default, no param needed
@@ -117,7 +122,10 @@ class GoogleBooksProvider(MetadataProvider):
f"{options.query}:{options.search_type.value}:{options.sort.value}:"
f"{options.language}:{options.limit}:{options.page}:{fields_key}"
)
return self._search_cached(cache_key, options)
try:
return self._search_cached(cache_key, options)
except _GoogleBooksRequestError:
return []
@cacheable(
ttl_key="METADATA_CACHE_SEARCH_TTL",
@@ -166,18 +174,19 @@ class GoogleBooksProvider(MetadataProvider):
if options.language:
params["langRestrict"] = options.language
result = self._make_request("/volumes", params)
if result is None:
raise _GoogleBooksRequestError
books: list[BookMetadata] = []
try:
result = self._make_request("/volumes", params)
if result:
items = result.get("items", [])
items = result.get("items", [])
for item in items:
book = self._parse_volume(item)
if book:
books.append(book)
for item in items:
book = self._parse_volume(item)
if book:
books.append(book)
logger.info("Google Books search '%s' returned %s results", query, len(books))
logger.info("Google Books search '%s' returned %s results", query, len(books))
except Exception:
logger.exception("Google Books search error")
+53
View File
@@ -1,6 +1,59 @@
import requests
from shelfmark.core.cache import get_metadata_cache
from shelfmark.metadata_providers import MetadataSearchOptions
from shelfmark.metadata_providers.googlebooks import GoogleBooksProvider
class _GoogleBooksResponse:
def __init__(self, payload):
self._payload = payload
def raise_for_status(self):
return None
def json(self):
return self._payload
class _FlakyGoogleBooksSession:
def __init__(self):
self.calls = 0
def get(self, *args, **kwargs):
self.calls += 1
if self.calls == 1:
raise requests.Timeout
return _GoogleBooksResponse(
{
"items": [
{
"id": "volume-1",
"volumeInfo": {
"title": "Recovered Book",
"authors": ["Alice Author"],
},
}
]
}
)
def test_googlebooks_search_does_not_cache_request_failures():
get_metadata_cache().clear()
provider = GoogleBooksProvider(api_key="test-key")
session = _FlakyGoogleBooksSession()
provider.session = session
options = MetadataSearchOptions(query="Recovered Book")
assert provider.search(options) == []
result = provider.search(options)
assert session.calls == 2
assert [book.title for book in result] == ["Recovered Book"]
class TestGoogleBooksParseVolume:
def test_parse_volume_returns_metadata_for_valid_payload(self):
provider = GoogleBooksProvider(api_key="test-key")