Enhance buscar-bibliografia function with Open Library integration and parameter handling
This commit is contained in:
@@ -6,6 +6,11 @@ import { HttpError, sendError, sendSuccess } from "../_shared/utils.ts";
|
||||
console.log("Starting buscar-bibliografia function");
|
||||
|
||||
type GoogleBooksVolume = Record<string, unknown>;
|
||||
type OpenLibraryDoc = Record<string, unknown>;
|
||||
|
||||
type EndpointResult =
|
||||
| { endpoint: "google"; item: GoogleBooksVolume }
|
||||
| { endpoint: "open_library"; item: OpenLibraryDoc };
|
||||
|
||||
interface GoogleBooksVolumesListResponse {
|
||||
kind?: string;
|
||||
@@ -13,17 +18,54 @@ interface GoogleBooksVolumesListResponse {
|
||||
items?: GoogleBooksVolume[];
|
||||
}
|
||||
|
||||
interface OpenLibrarySearchResponse {
|
||||
num_found?: number;
|
||||
start?: number;
|
||||
docs?: OpenLibraryDoc[];
|
||||
}
|
||||
|
||||
const ISO6391 = z.string().regex(
|
||||
/^[a-z]{2}$/i,
|
||||
"Debe ser ISO 639-1 (2 letras)",
|
||||
);
|
||||
const ISO6392 = z.string().regex(
|
||||
/^[a-z]{3}$/i,
|
||||
"Debe ser ISO 639-2 (3 letras)",
|
||||
);
|
||||
|
||||
const SearchTermsSchema = z
|
||||
.object({
|
||||
q: z.string().min(1, "q es requerido"),
|
||||
maxResults: z.number().int().min(0).max(40),
|
||||
orderBy: z.enum(["newest", "relevance"]).optional(),
|
||||
})
|
||||
.passthrough();
|
||||
.strict();
|
||||
|
||||
const GoogleParamsSchema = z
|
||||
.object({
|
||||
orderBy: z.enum(["newest", "relevance"]).optional(),
|
||||
langRestrict: ISO6391.optional(),
|
||||
startIndex: z.number().int().min(0).optional(),
|
||||
})
|
||||
.passthrough()
|
||||
.optional()
|
||||
.default({});
|
||||
|
||||
const OpenLibraryParamsSchema = z
|
||||
.object({
|
||||
language: ISO6392.optional(),
|
||||
page: z.number().int().min(1).optional(),
|
||||
sort: z.string().optional(),
|
||||
})
|
||||
.passthrough()
|
||||
.optional()
|
||||
.default({});
|
||||
|
||||
const BodySchema = z
|
||||
.object({
|
||||
searchTerms: SearchTermsSchema,
|
||||
|
||||
// Parámetros por endpoint
|
||||
google: GoogleParamsSchema,
|
||||
openLibrary: OpenLibraryParamsSchema,
|
||||
})
|
||||
.strict();
|
||||
|
||||
@@ -57,6 +99,40 @@ function buildUrlWithSearchTerms(
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function pickSerializableParams(
|
||||
input: Record<string, unknown>,
|
||||
): Record<
|
||||
string,
|
||||
string | number | boolean | Array<string | number | boolean>
|
||||
> {
|
||||
const out: Record<
|
||||
string,
|
||||
string | number | boolean | Array<string | number | boolean>
|
||||
> = {};
|
||||
|
||||
for (const [key, value] of Object.entries(input)) {
|
||||
if (value === undefined || value === null) continue;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const arr = value
|
||||
.filter((v) => v !== undefined && v !== null)
|
||||
.filter((v) =>
|
||||
["string", "number", "boolean"].includes(typeof v)
|
||||
) as Array<
|
||||
string | number | boolean
|
||||
>;
|
||||
if (arr.length) out[key] = arr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (["string", "number", "boolean"].includes(typeof value)) {
|
||||
out[key] = value as string | number | boolean;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Deno.serve(async (req: Request): Promise<Response> => {
|
||||
const url = new URL(req.url);
|
||||
const functionName = url.pathname.split("/").pop();
|
||||
@@ -117,17 +193,40 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
||||
}
|
||||
|
||||
const baseUrl = "https://www.googleapis.com/books/v1/volumes";
|
||||
const requestUrl = buildUrlWithSearchTerms(baseUrl, {
|
||||
const googleParams = pickSerializableParams({
|
||||
...body.google,
|
||||
...body.searchTerms,
|
||||
key: GOOGLE_API_KEY,
|
||||
maxResults: 20,
|
||||
startIndex: (body.google as Record<string, unknown>)?.startIndex ?? 0,
|
||||
});
|
||||
// No mandar parámetros exclusivos de Open Library a Google
|
||||
delete (googleParams as Record<string, unknown>)["page"];
|
||||
delete (googleParams as Record<string, unknown>)["language"];
|
||||
|
||||
const googleResp = await fetch(requestUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
const googleUrl = buildUrlWithSearchTerms(baseUrl, googleParams);
|
||||
|
||||
const openLibraryBaseUrl = "https://openlibrary.org/search.json";
|
||||
const openLibraryParams = pickSerializableParams({
|
||||
...body.openLibrary,
|
||||
q: body.searchTerms.q,
|
||||
limit: 20,
|
||||
page: (body.openLibrary as Record<string, unknown>)?.page ?? 1,
|
||||
});
|
||||
// No mandar parámetros exclusivos de Google a Open Library
|
||||
delete (openLibraryParams as Record<string, unknown>)["langRestrict"];
|
||||
delete (openLibraryParams as Record<string, unknown>)["orderBy"];
|
||||
delete (openLibraryParams as Record<string, unknown>)["startIndex"];
|
||||
|
||||
const openLibraryUrl = buildUrlWithSearchTerms(
|
||||
openLibraryBaseUrl,
|
||||
openLibraryParams,
|
||||
);
|
||||
|
||||
const [googleResp, openLibraryResp] = await Promise.all([
|
||||
fetch(googleUrl, { headers: { Accept: "application/json" } }),
|
||||
fetch(openLibraryUrl, { headers: { Accept: "application/json" } }),
|
||||
]);
|
||||
|
||||
if (!googleResp.ok) {
|
||||
const text = await googleResp.text().catch(() => "");
|
||||
@@ -143,10 +242,40 @@ Deno.serve(async (req: Request): Promise<Response> => {
|
||||
);
|
||||
}
|
||||
|
||||
const data = (await googleResp.json()) as GoogleBooksVolumesListResponse;
|
||||
const items = Array.isArray(data?.items) ? data.items : [];
|
||||
if (!openLibraryResp.ok) {
|
||||
const text = await openLibraryResp.text().catch(() => "");
|
||||
throw new HttpError(
|
||||
502,
|
||||
"Error al consultar Open Library.",
|
||||
"OPEN_LIBRARY_REQUEST_FAILED",
|
||||
{
|
||||
status: openLibraryResp.status,
|
||||
statusText: openLibraryResp.statusText,
|
||||
body: text || null,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return sendSuccess(items);
|
||||
const googleData =
|
||||
(await googleResp.json()) as GoogleBooksVolumesListResponse;
|
||||
const openLibraryData =
|
||||
(await openLibraryResp.json()) as OpenLibrarySearchResponse;
|
||||
|
||||
const googleItems = Array.isArray(googleData?.items)
|
||||
? googleData.items.slice(0, 20)
|
||||
: [];
|
||||
const openLibraryDocs = Array.isArray(openLibraryData?.docs)
|
||||
? openLibraryData.docs.slice(0, 20)
|
||||
: [];
|
||||
|
||||
const results: EndpointResult[] = [
|
||||
...googleItems.map((item) => ({ endpoint: "google", item } as const)),
|
||||
...openLibraryDocs.map((
|
||||
item,
|
||||
) => ({ endpoint: "open_library", item } as const)),
|
||||
];
|
||||
|
||||
return sendSuccess(results);
|
||||
} catch (error) {
|
||||
if (error instanceof HttpError) {
|
||||
console.error(
|
||||
|
||||
Reference in New Issue
Block a user