mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 08:21:39 -06:00
fix(metadata): restore Hardcover title and author matching
Use Hardcover's default search configuration because custom fields currently return a payload-level error. Surface future search errors in logs so failures remain diagnosable. Fixes #1123
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
|
||||
import { EDITION_DISPLAY_LIMIT, HardcoverBookMatchService } from './hardcover-book-match.service';
|
||||
|
||||
@@ -37,6 +37,10 @@ describe('HardcoverBookMatchService', () => {
|
||||
mockRepo.upsertBookState.mockResolvedValue({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('returns cached match when state exists and no error', async () => {
|
||||
mockRepo.findBookState.mockResolvedValue({
|
||||
hardcoverBookId: 100,
|
||||
@@ -189,6 +193,32 @@ describe('HardcoverBookMatchService', () => {
|
||||
expect.stringContaining('query SearchBooks'),
|
||||
expect.objectContaining({ query: 'Test Book Test Author' }),
|
||||
);
|
||||
const searchQuery = mockClient.query.mock.calls[1]?.[2] as string;
|
||||
expect(searchQuery).toContain('error');
|
||||
expect(searchQuery).not.toContain('fields:');
|
||||
expect(searchQuery).not.toContain('weights:');
|
||||
});
|
||||
|
||||
it('logs payload-level errors returned by Hardcover search', async () => {
|
||||
mockRepo.findBookState.mockResolvedValue(undefined);
|
||||
mockClient.query.mockResolvedValueOnce({ books: [] }).mockResolvedValueOnce({
|
||||
search: {
|
||||
error: 'Number of values in `num_typos` does not match number of `query_by` fields.',
|
||||
ids: null,
|
||||
},
|
||||
});
|
||||
const service = makeService();
|
||||
const logger = Reflect.get(service, 'logger') as { warn: (message: string) => void };
|
||||
const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => undefined);
|
||||
|
||||
await expect(service.matchBook(1, 'tok', baseBook)).resolves.toBeNull();
|
||||
|
||||
expect(mockClient.query).toHaveBeenCalledTimes(2);
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
expect.stringMatching(
|
||||
/^\[hardcover\.book_match\] \[fail\] userId=1 bookId=42 method=title_author durationMs=\d+ errorClass=SearchError error="Number of values in `num_typos` does not match number of `query_by` fields\." - title lookup failed$/,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('preserves Hardcover search ranking when hydrating title matches', async () => {
|
||||
|
||||
@@ -46,9 +46,8 @@ query SearchBooks($query: String!) {
|
||||
query_type: "Book"
|
||||
per_page: 5
|
||||
page: 1
|
||||
fields: "title,author_names,alternative_titles"
|
||||
weights: "5,2,1"
|
||||
) {
|
||||
error
|
||||
ids
|
||||
}
|
||||
}`;
|
||||
@@ -200,7 +199,8 @@ function mapEditionForDisplay(edition: HardcoverEditionDisplayRow): HardcoverEdi
|
||||
|
||||
interface SearchBooksResult {
|
||||
search?: {
|
||||
ids?: number[];
|
||||
error?: string | null;
|
||||
ids?: number[] | null;
|
||||
} | null;
|
||||
}
|
||||
|
||||
@@ -354,10 +354,18 @@ export class HardcoverBookMatchService {
|
||||
author: string,
|
||||
book: BookSyncData,
|
||||
): Promise<HardcoverBookMatch | null> {
|
||||
const startedAt = Date.now();
|
||||
try {
|
||||
const searchData = await this.client.query<SearchBooksResult>(userId, token, SEARCH_BOOKS_QUERY, {
|
||||
query: `${title} ${author}`,
|
||||
});
|
||||
if (searchData.search?.error) {
|
||||
const error = sanitizeLogValue(searchData.search.error);
|
||||
this.logger.warn(
|
||||
`[hardcover.book_match] [fail] userId=${userId} bookId=${book.bookId} method=title_author durationMs=${Date.now() - startedAt} errorClass=SearchError error="${error}" - title lookup failed`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
const ids = searchData.search?.ids?.filter((id) => Number.isInteger(id)).slice(0, 5) ?? [];
|
||||
if (ids.length === 0) return null;
|
||||
|
||||
@@ -374,9 +382,10 @@ export class HardcoverBookMatchService {
|
||||
matchMethod: 'title',
|
||||
};
|
||||
} catch (err) {
|
||||
const errorClass = err instanceof Error ? err.constructor.name : 'Error';
|
||||
const error = sanitizeLogValue(err instanceof Error ? err.message : String(err));
|
||||
this.logger.warn(
|
||||
`[hardcover.book_match] [fail] userId=${userId} bookId=${book.bookId} method=title_author error="${error}" - title lookup failed`,
|
||||
`[hardcover.book_match] [fail] userId=${userId} bookId=${book.bookId} method=title_author durationMs=${Date.now() - startedAt} errorClass=${errorClass} error="${error}" - title lookup failed`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user