feat: expose unstable mode in api routes

This commit is contained in:
2026-04-27 02:49:35 -04:00
parent 224e83ac4c
commit 3c38232cd5
4 changed files with 154 additions and 18 deletions

View File

@@ -37,17 +37,34 @@ export async function ebayRoute(req: Request): Promise<Response> {
const maxItemsParam = reqUrl.searchParams.get("maxItems"); const maxItemsParam = reqUrl.searchParams.get("maxItems");
const maxItems = maxItemsParam ? parseInt(maxItemsParam, 10) : undefined; const maxItems = maxItemsParam ? parseInt(maxItemsParam, 10) : undefined;
const items = await fetchEbayItems(SEARCH_QUERY, 1, { const hideUnstableResults =
minPrice, reqUrl.searchParams.get("unstableFilter") === "true";
maxPrice, const items = hideUnstableResults
strictMode, ? await fetchEbayItems(
exclusions, SEARCH_QUERY,
keywords, 1,
buyItNowOnly, {
canadaOnly, minPrice,
}); maxPrice,
strictMode,
exclusions,
keywords,
buyItNowOnly,
canadaOnly,
},
{ hideUnstableResults: true },
)
: await fetchEbayItems(SEARCH_QUERY, 1, {
minPrice,
maxPrice,
strictMode,
exclusions,
keywords,
buyItNowOnly,
canadaOnly,
});
const results = maxItems ? items.slice(0, maxItems) : items; const results = hideUnstableResults || !maxItems ? items : items.slice(0, maxItems);
if (!results || results.length === 0) if (!results || results.length === 0)
return Response.json( return Response.json(

View File

@@ -20,9 +20,15 @@ export async function facebookRoute(req: Request): Promise<Response> {
const LOCATION = reqUrl.searchParams.get("location") || "toronto"; const LOCATION = reqUrl.searchParams.get("location") || "toronto";
const maxItemsParam = reqUrl.searchParams.get("maxItems"); const maxItemsParam = reqUrl.searchParams.get("maxItems");
const maxItems = maxItemsParam ? parseInt(maxItemsParam, 10) : 25; const maxItems = maxItemsParam ? parseInt(maxItemsParam, 10) : 25;
const hideUnstableResults =
reqUrl.searchParams.get("unstableFilter") === "true";
try { try {
const items = await fetchFacebookItems(SEARCH_QUERY, 1, LOCATION, maxItems); const items = hideUnstableResults
? await fetchFacebookItems(SEARCH_QUERY, 1, LOCATION, maxItems, {
hideUnstableResults: true,
})
: await fetchFacebookItems(SEARCH_QUERY, 1, LOCATION, maxItems);
if (!items || items.length === 0) if (!items || items.length === 0)
return Response.json( return Response.json(
{ message: "Search didn't return any results!" }, { message: "Search didn't return any results!" },

View File

@@ -23,6 +23,8 @@ export async function kijijiRoute(req: Request): Promise<Response> {
const priceMin = priceMinParam ? parseInt(priceMinParam, 10) : undefined; const priceMin = priceMinParam ? parseInt(priceMinParam, 10) : undefined;
const priceMaxParam = reqUrl.searchParams.get("priceMax"); const priceMaxParam = reqUrl.searchParams.get("priceMax");
const priceMax = priceMaxParam ? parseInt(priceMaxParam, 10) : undefined; const priceMax = priceMaxParam ? parseInt(priceMaxParam, 10) : undefined;
const hideUnstableResults =
reqUrl.searchParams.get("unstableFilter") === "true";
const searchOptions = { const searchOptions = {
location: reqUrl.searchParams.get("location") || undefined, location: reqUrl.searchParams.get("location") || undefined,
@@ -45,13 +47,22 @@ export async function kijijiRoute(req: Request): Promise<Response> {
}; };
try { try {
const items = await fetchKijijiItems( const items = hideUnstableResults
SEARCH_QUERY, ? await fetchKijijiItems(
4, // 4 requests per second for faster scraping SEARCH_QUERY,
"https://www.kijiji.ca", 4, // 4 requests per second for faster scraping
searchOptions, "https://www.kijiji.ca",
{}, searchOptions,
); {},
{ hideUnstableResults: true },
)
: await fetchKijijiItems(
SEARCH_QUERY,
4, // 4 requests per second for faster scraping
"https://www.kijiji.ca",
searchOptions,
{},
);
if (!items) if (!items)
return Response.json( return Response.json(
{ message: "Search didn't return any results!" }, { message: "Search didn't return any results!" },

View File

@@ -2,10 +2,12 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
const fetchFacebookItems = mock(() => Promise.resolve([{ title: "item" }])); const fetchFacebookItems = mock(() => Promise.resolve([{ title: "item" }]));
const fetchEbayItems = mock(() => Promise.resolve([{ title: "item" }])); const fetchEbayItems = mock(() => Promise.resolve([{ title: "item" }]));
const fetchKijijiItems = mock(() => Promise.resolve([{ title: "item" }]));
mock.module("@marketplace-scrapers/core", () => ({ mock.module("@marketplace-scrapers/core", () => ({
fetchFacebookItems, fetchFacebookItems,
fetchEbayItems, fetchEbayItems,
fetchKijijiItems,
})); }));
describe("API routes", () => { describe("API routes", () => {
@@ -18,11 +20,16 @@ describe("API routes", () => {
fetchEbayItems.mockImplementation(() => fetchEbayItems.mockImplementation(() =>
Promise.resolve([{ title: "item" }]), Promise.resolve([{ title: "item" }]),
); );
fetchKijijiItems.mockReset();
fetchKijijiItems.mockImplementation(() =>
Promise.resolve([{ title: "item" }]),
);
}); });
afterEach(() => { afterEach(() => {
fetchFacebookItems.mockClear(); fetchFacebookItems.mockClear();
fetchEbayItems.mockClear(); fetchEbayItems.mockClear();
fetchKijijiItems.mockClear();
}); });
test("facebookRoute ignores cookies query parameter", async () => { test("facebookRoute ignores cookies query parameter", async () => {
@@ -56,4 +63,99 @@ describe("API routes", () => {
canadaOnly: true, canadaOnly: true,
}); });
}); });
test("kijijiRoute ignores cookies query parameter", async () => {
const { kijijiRoute } = await import("../src/routes/kijiji");
await kijijiRoute(
new Request(
"http://localhost/api/kijiji?q=laptop&cookies=s%3D1&maxPages=3",
),
);
expect(fetchKijijiItems).toHaveBeenCalledWith(
"laptop",
4,
"https://www.kijiji.ca",
{
location: undefined,
category: undefined,
keywords: undefined,
sortBy: null,
sortOrder: null,
maxPages: 3,
priceMin: undefined,
priceMax: undefined,
cookies: "s=1",
},
{},
);
});
test("facebookRoute forwards unstableFilter=true to core", async () => {
const { facebookRoute } = await import("../src/routes/facebook");
await facebookRoute(
new Request(
"http://localhost/api/facebook?q=laptop&location=toronto&maxItems=3&unstableFilter=true",
),
);
expect(fetchFacebookItems).toHaveBeenCalledWith("laptop", 1, "toronto", 3, {
hideUnstableResults: true,
});
});
test("ebayRoute forwards unstableFilter=true to core", async () => {
const { ebayRoute } = await import("../src/routes/ebay");
await ebayRoute(
new Request(
"http://localhost/api/ebay?q=laptop&buyItNowOnly=true&unstableFilter=true",
),
);
expect(fetchEbayItems).toHaveBeenCalledWith("laptop", 1, {
minPrice: undefined,
maxPrice: undefined,
strictMode: false,
exclusions: [],
keywords: ["laptop"],
buyItNowOnly: true,
canadaOnly: true,
}, {
hideUnstableResults: true,
});
});
test("kijijiRoute forwards unstableFilter=true to core", async () => {
const { kijijiRoute } = await import("../src/routes/kijiji");
await kijijiRoute(
new Request(
"http://localhost/api/kijiji?q=laptop&maxPages=5&unstableFilter=true",
),
);
expect(fetchKijijiItems).toHaveBeenCalledWith(
"laptop",
4,
"https://www.kijiji.ca",
{
location: undefined,
category: undefined,
keywords: undefined,
sortBy: null,
sortOrder: null,
maxPages: 5,
priceMin: undefined,
priceMax: undefined,
cookies: undefined,
},
{},
{
hideUnstableResults: true,
},
);
});
}); });