fix: validate route params and reduce duplication
This commit is contained in:
@@ -37,32 +37,29 @@ 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;
|
||||||
|
if (maxItemsParam && Number.isNaN(maxItems)) {
|
||||||
|
return Response.json(
|
||||||
|
{ message: "Invalid maxItems parameter" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
const hideUnstableResults =
|
const hideUnstableResults =
|
||||||
reqUrl.searchParams.get("unstableFilter") === "true";
|
reqUrl.searchParams.get("unstableFilter") === "true";
|
||||||
const items = hideUnstableResults
|
const opts = {
|
||||||
? await fetchEbayItems(
|
minPrice,
|
||||||
|
maxPrice,
|
||||||
|
strictMode,
|
||||||
|
exclusions,
|
||||||
|
keywords,
|
||||||
|
buyItNowOnly,
|
||||||
|
canadaOnly,
|
||||||
|
};
|
||||||
|
const items = await fetchEbayItems(
|
||||||
SEARCH_QUERY,
|
SEARCH_QUERY,
|
||||||
1,
|
1,
|
||||||
{
|
opts,
|
||||||
minPrice,
|
...(hideUnstableResults ? [{ hideUnstableResults: true }] : []),
|
||||||
maxPrice,
|
);
|
||||||
strictMode,
|
|
||||||
exclusions,
|
|
||||||
keywords,
|
|
||||||
buyItNowOnly,
|
|
||||||
canadaOnly,
|
|
||||||
},
|
|
||||||
{ hideUnstableResults: true },
|
|
||||||
)
|
|
||||||
: await fetchEbayItems(SEARCH_QUERY, 1, {
|
|
||||||
minPrice,
|
|
||||||
maxPrice,
|
|
||||||
strictMode,
|
|
||||||
exclusions,
|
|
||||||
keywords,
|
|
||||||
buyItNowOnly,
|
|
||||||
canadaOnly,
|
|
||||||
});
|
|
||||||
|
|
||||||
const isEmpty = hideUnstableResults
|
const isEmpty = hideUnstableResults
|
||||||
? items.results.length === 0 && items.unstableResults.length === 0
|
? items.results.length === 0 && items.unstableResults.length === 0
|
||||||
|
|||||||
@@ -20,15 +20,23 @@ 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;
|
||||||
|
if (maxItemsParam && Number.isNaN(maxItems)) {
|
||||||
|
return Response.json(
|
||||||
|
{ message: "Invalid maxItems parameter" },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
const hideUnstableResults =
|
const hideUnstableResults =
|
||||||
reqUrl.searchParams.get("unstableFilter") === "true";
|
reqUrl.searchParams.get("unstableFilter") === "true";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const items = hideUnstableResults
|
const items = await fetchFacebookItems(
|
||||||
? await fetchFacebookItems(SEARCH_QUERY, 1, LOCATION, maxItems, {
|
SEARCH_QUERY,
|
||||||
hideUnstableResults: true,
|
1,
|
||||||
})
|
LOCATION,
|
||||||
: await fetchFacebookItems(SEARCH_QUERY, 1, LOCATION, maxItems);
|
maxItems,
|
||||||
|
...(hideUnstableResults ? [{ hideUnstableResults: true }] : []),
|
||||||
|
);
|
||||||
|
|
||||||
const isEmpty = hideUnstableResults
|
const isEmpty = hideUnstableResults
|
||||||
? items.results.length === 0 && items.unstableResults.length === 0
|
? items.results.length === 0 && items.unstableResults.length === 0
|
||||||
|
|||||||
@@ -30,16 +30,16 @@ export async function kijijiRoute(req: Request): Promise<Response> {
|
|||||||
location: reqUrl.searchParams.get("location") || undefined,
|
location: reqUrl.searchParams.get("location") || undefined,
|
||||||
category: reqUrl.searchParams.get("category") || undefined,
|
category: reqUrl.searchParams.get("category") || undefined,
|
||||||
keywords: reqUrl.searchParams.get("keywords") || undefined,
|
keywords: reqUrl.searchParams.get("keywords") || undefined,
|
||||||
sortBy: reqUrl.searchParams.get("sortBy") as
|
sortBy: (reqUrl.searchParams.get("sortBy") as
|
||||||
| "relevancy"
|
| "relevancy"
|
||||||
| "date"
|
| "date"
|
||||||
| "price"
|
| "price"
|
||||||
| "distance"
|
| "distance"
|
||||||
| undefined,
|
| undefined) || undefined,
|
||||||
sortOrder: reqUrl.searchParams.get("sortOrder") as
|
sortOrder: (reqUrl.searchParams.get("sortOrder") as
|
||||||
| "desc"
|
| "desc"
|
||||||
| "asc"
|
| "asc"
|
||||||
| undefined,
|
| undefined) || undefined,
|
||||||
maxPages,
|
maxPages,
|
||||||
priceMin,
|
priceMin,
|
||||||
priceMax,
|
priceMax,
|
||||||
@@ -47,21 +47,13 @@ export async function kijijiRoute(req: Request): Promise<Response> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const items = hideUnstableResults
|
const items = await fetchKijijiItems(
|
||||||
? await fetchKijijiItems(
|
|
||||||
SEARCH_QUERY,
|
|
||||||
4, // 4 requests per second for faster scraping
|
|
||||||
"https://www.kijiji.ca",
|
|
||||||
searchOptions,
|
|
||||||
{},
|
|
||||||
{ hideUnstableResults: true },
|
|
||||||
)
|
|
||||||
: await fetchKijijiItems(
|
|
||||||
SEARCH_QUERY,
|
SEARCH_QUERY,
|
||||||
4, // 4 requests per second for faster scraping
|
4, // 4 requests per second for faster scraping
|
||||||
"https://www.kijiji.ca",
|
"https://www.kijiji.ca",
|
||||||
searchOptions,
|
searchOptions,
|
||||||
{},
|
{},
|
||||||
|
...(hideUnstableResults ? [{ hideUnstableResults: true }] : []),
|
||||||
);
|
);
|
||||||
|
|
||||||
const isEmpty = hideUnstableResults
|
const isEmpty = hideUnstableResults
|
||||||
|
|||||||
@@ -26,12 +26,6 @@ describe("API routes", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
fetchFacebookItems.mockClear();
|
|
||||||
fetchEbayItems.mockClear();
|
|
||||||
fetchKijijiItems.mockClear();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("facebookRoute ignores cookies query parameter", async () => {
|
test("facebookRoute ignores cookies query parameter", async () => {
|
||||||
const { facebookRoute } = await import("../src/routes/facebook");
|
const { facebookRoute } = await import("../src/routes/facebook");
|
||||||
|
|
||||||
@@ -81,8 +75,8 @@ describe("API routes", () => {
|
|||||||
location: undefined,
|
location: undefined,
|
||||||
category: undefined,
|
category: undefined,
|
||||||
keywords: undefined,
|
keywords: undefined,
|
||||||
sortBy: null,
|
sortBy: undefined,
|
||||||
sortOrder: null,
|
sortOrder: undefined,
|
||||||
maxPages: 3,
|
maxPages: 3,
|
||||||
priceMin: undefined,
|
priceMin: undefined,
|
||||||
priceMax: undefined,
|
priceMax: undefined,
|
||||||
@@ -166,8 +160,8 @@ describe("API routes", () => {
|
|||||||
location: undefined,
|
location: undefined,
|
||||||
category: undefined,
|
category: undefined,
|
||||||
keywords: undefined,
|
keywords: undefined,
|
||||||
sortBy: null,
|
sortBy: undefined,
|
||||||
sortOrder: null,
|
sortOrder: undefined,
|
||||||
maxPages: 5,
|
maxPages: 5,
|
||||||
priceMin: undefined,
|
priceMin: undefined,
|
||||||
priceMax: undefined,
|
priceMax: undefined,
|
||||||
@@ -261,8 +255,8 @@ describe("API routes", () => {
|
|||||||
location: undefined,
|
location: undefined,
|
||||||
category: undefined,
|
category: undefined,
|
||||||
keywords: undefined,
|
keywords: undefined,
|
||||||
sortBy: null,
|
sortBy: undefined,
|
||||||
sortOrder: null,
|
sortOrder: undefined,
|
||||||
maxPages: 5,
|
maxPages: 5,
|
||||||
priceMin: undefined,
|
priceMin: undefined,
|
||||||
priceMax: undefined,
|
priceMax: undefined,
|
||||||
@@ -289,8 +283,8 @@ describe("API routes", () => {
|
|||||||
location: undefined,
|
location: undefined,
|
||||||
category: undefined,
|
category: undefined,
|
||||||
keywords: undefined,
|
keywords: undefined,
|
||||||
sortBy: null,
|
sortBy: undefined,
|
||||||
sortOrder: null,
|
sortOrder: undefined,
|
||||||
maxPages: 5,
|
maxPages: 5,
|
||||||
priceMin: undefined,
|
priceMin: undefined,
|
||||||
priceMax: undefined,
|
priceMax: undefined,
|
||||||
@@ -429,4 +423,53 @@ describe("API routes", () => {
|
|||||||
expect(body.results[0].title).toBe("a");
|
expect(body.results[0].title).toBe("a");
|
||||||
expect(body.unstableResults[0].title).toBe("d");
|
expect(body.unstableResults[0].title).toBe("d");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("ebayRoute returns 404 when unstable results are empty", async () => {
|
||||||
|
const { ebayRoute } = await import("../src/routes/ebay");
|
||||||
|
|
||||||
|
fetchEbayItems.mockImplementation(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
results: [],
|
||||||
|
unstableResults: [],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const response = await ebayRoute(
|
||||||
|
new Request(
|
||||||
|
"http://localhost/api/ebay?q=laptop&unstableFilter=true",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toBe(404);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Search didn't return any results!");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("ebayRoute returns 400 for invalid maxItems", async () => {
|
||||||
|
const { ebayRoute } = await import("../src/routes/ebay");
|
||||||
|
|
||||||
|
const response = await ebayRoute(
|
||||||
|
new Request(
|
||||||
|
"http://localhost/api/ebay?q=laptop&maxItems=abc",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toBe(400);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Invalid maxItems parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("facebookRoute returns 400 for invalid maxItems", async () => {
|
||||||
|
const { facebookRoute } = await import("../src/routes/facebook");
|
||||||
|
|
||||||
|
const response = await facebookRoute(
|
||||||
|
new Request(
|
||||||
|
"http://localhost/api/facebook?q=laptop&maxItems=abc",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.status).toBe(400);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Invalid maxItems parameter");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user