pub async fn paper_search(
base: &Url,
contact_email: &str,
query: &PaperSearchQuery,
ctx: &FetchContext,
) -> Result<PaperSearchResults, FetchError>Expand description
Run a discovery search against OpenAlex and return ranked candidates.
base is the OpenAlex API base URL (production
https://api.openalex.org; tests inject a wiremock origin, mirroring
the DOIGET_OPENALEX_BASE override the CLI honors). contact_email
opts into the polite pool via ?mailto= when non-empty.
When query.author / query.venue / query.publisher are set, this
first issues one ?search= lookup each against /authors /
/sources / /publishers to resolve the name to an OpenAlex ID, then
filters /works by that ID. Every call reuses ctx.http (allowlisted,
HTTPS-only in production), ctx.rate_limiter, and ctx.log (one
Metadata/Fetch provenance row per request). Never fetches a PDF
(ADR-0031 D3).
§Caller-side validation
This is permissive on the query shape — boundary validation is the
caller’s job (the CLI does it; an MCP tool should too). Specifically:
query.limit is clamped to 1..=200 (not rejected), and an
inverted year range (from_year > to_year) is passed through and
yields an empty result set rather than an error. Direct callers
that want a typed error for those should pre-validate.
§Errors
Returns FetchError::Http for transport / allowlist failures,
FetchError::NotFound when an author/venue/publisher name resolves
to nothing, FetchError::Ambiguous when such a name matches several
entities with no clear winner (carries a candidate listing),
FetchError::SourceSchema when a response is not a JSON object
carrying a results array, and propagates a provenance-log append
failure (fail-closed).