mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Remove page's prev & weird reverse handling
This commit is contained in:
parent
371d9148f4
commit
6e293efc2b
@ -3,7 +3,7 @@ import { eq, or, type Column, and, gt, lt } from "drizzle-orm";
|
||||
|
||||
type Table<Name extends string> = Record<Name, Column>;
|
||||
|
||||
type After = [boolean, ...[string | number | boolean | undefined]];
|
||||
type After = (string | number | boolean | undefined)[];
|
||||
|
||||
// Create a filter (where) expression on the query to skip everything before/after the referenceID.
|
||||
// The generalized expression for this in pseudocode is:
|
||||
@ -29,7 +29,7 @@ export const keysetPaginate = <
|
||||
sort: Sort<T, Remap>;
|
||||
}) => {
|
||||
if (!after) return undefined;
|
||||
const [reverse, ...cursor]: After = JSON.parse(
|
||||
const cursor: After = JSON.parse(
|
||||
Buffer.from(after, "base64").toString("utf-8"),
|
||||
);
|
||||
|
||||
@ -40,7 +40,7 @@ export const keysetPaginate = <
|
||||
let where = undefined;
|
||||
let previous = undefined;
|
||||
for (const [i, by] of [...sort, pkSort].entries()) {
|
||||
const cmp = by.desc !== reverse ? lt : gt;
|
||||
const cmp = by.desc ? lt : gt;
|
||||
where = or(where, and(previous, cmp(table[by.key], cursor[i])));
|
||||
previous = and(previous, eq(table[by.key], cursor[i]));
|
||||
}
|
||||
@ -48,18 +48,7 @@ export const keysetPaginate = <
|
||||
return where;
|
||||
};
|
||||
|
||||
export const generateAfter = (
|
||||
cursor: any,
|
||||
sort: Sort<any, any>,
|
||||
reverse?: boolean,
|
||||
) => {
|
||||
const ret = [
|
||||
reverse ?? false,
|
||||
...sort.map((by) => cursor[by.key]),
|
||||
cursor.pk,
|
||||
];
|
||||
export const generateAfter = (cursor: any, sort: Sort<any, any>) => {
|
||||
const ret = [...sort.map((by) => cursor[by.key]), cursor.pk];
|
||||
return Buffer.from(JSON.stringify(ret), "utf-8").toString("base64url");
|
||||
};
|
||||
|
||||
const reverseStart = Buffer.from("[true,", "utf-8").toString("base64url");
|
||||
export const isReverse = (x: string) => x.startsWith(reverseStart);
|
||||
|
@ -1,14 +1,13 @@
|
||||
import type { ObjectOptions } from "@sinclair/typebox";
|
||||
import { t, type TSchema } from "elysia";
|
||||
import type { Sort } from "./sort";
|
||||
import { generateAfter, isReverse } from "./keyset-paginate";
|
||||
import { generateAfter } from "./keyset-paginate";
|
||||
|
||||
export const Page = <T extends TSchema>(schema: T, options?: ObjectOptions) =>
|
||||
t.Object(
|
||||
{
|
||||
items: t.Array(schema),
|
||||
this: t.String({ format: "uri" }),
|
||||
prev: t.Nullable(t.String({ format: "uri" })),
|
||||
next: t.Nullable(t.String({ format: "uri" })),
|
||||
},
|
||||
options,
|
||||
@ -18,27 +17,12 @@ export const createPage = <T>(
|
||||
items: T[],
|
||||
{ url, sort, limit }: { url: string; sort: Sort<any, any>; limit: number },
|
||||
) => {
|
||||
let prev: string | null = null;
|
||||
let next: string | null = null;
|
||||
|
||||
const uri = new URL(url);
|
||||
const after = uri.searchParams.get("after");
|
||||
const reverse = after && isReverse(after) ? 1 : 0;
|
||||
|
||||
const has = [
|
||||
// prev
|
||||
items.length > 0 && after,
|
||||
// next
|
||||
items.length === limit && limit > 0,
|
||||
];
|
||||
|
||||
if (has[0 + reverse]) {
|
||||
uri.searchParams.set("after", generateAfter(items[0], sort, true));
|
||||
prev = uri.toString();
|
||||
}
|
||||
if (has[1 - reverse]) {
|
||||
if (items.length === limit && limit > 0) {
|
||||
const uri = new URL(url);
|
||||
uri.searchParams.set("after", generateAfter(items[items.length - 1], sort));
|
||||
next = uri.toString();
|
||||
}
|
||||
return { items, this: url, prev, next };
|
||||
return { items, this: url, next };
|
||||
};
|
||||
|
@ -78,23 +78,47 @@ describe("Get all movies", () => {
|
||||
|
||||
expectStatus(resp, body).toBe(422);
|
||||
expect(body).toMatchObject({
|
||||
details: expect.objectContaining( {
|
||||
in: "slug eq gt bubble",
|
||||
}),
|
||||
details: expect.anything(),
|
||||
message: "Invalid filter: slug eq gt bubble.",
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
it("Limit 1, default sort", async () => {
|
||||
it("Limit 2, default sort", async () => {
|
||||
const [resp, body] = await getMovies({
|
||||
limit: 1,
|
||||
limit: 2,
|
||||
langs: "en",
|
||||
});
|
||||
|
||||
expectStatus(resp, body).toBe(200);
|
||||
expect(body).toMatchObject({
|
||||
items: [bubble],
|
||||
this: "",
|
||||
items: [
|
||||
expect.objectContaining({ slug: bubble.slug }),
|
||||
expect.objectContaining({ slug: dune.slug }),
|
||||
],
|
||||
this: "http://localhost/movies?limit=2",
|
||||
// we can't have the exact after since it contains the pk that changes with every tests.
|
||||
next: expect.stringContaining(
|
||||
"http://localhost/movies?limit=2&after=WyJkdW5lIiw0",
|
||||
),
|
||||
});
|
||||
});
|
||||
it("Limit 2, default sort, page 2", async () => {
|
||||
let [resp, body] = await getMovies({
|
||||
limit: 2,
|
||||
langs: "en",
|
||||
});
|
||||
expectStatus(resp, body).toBe(200);
|
||||
|
||||
resp = await app.handle(new Request(body.next));
|
||||
body = await resp.json();
|
||||
|
||||
expectStatus(resp, body).toBe(200);
|
||||
expect(body).toMatchObject({
|
||||
items: [expect.objectContaining({ slug: dune1984.slug })],
|
||||
this: expect.stringContaining(
|
||||
"http://localhost/movies?limit=2&after=WyJkdW5lIiw0",
|
||||
),
|
||||
next: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -110,7 +110,7 @@ describe("Movie seeding", () => {
|
||||
expect(existing).toMatchObject({ slug: dune.slug, startAir: dune.airDate });
|
||||
|
||||
const [resp, body] = await createMovie({ ...dune, airDate: "2158-12-13" });
|
||||
expectStatus(resp, body).toBe(200);
|
||||
expectStatus(resp, body).toBe(201);
|
||||
expect(body.id).toBeString();
|
||||
expect(body.slug).toBe("dune-2158");
|
||||
});
|
||||
@ -224,7 +224,7 @@ describe("Movie seeding", () => {
|
||||
});
|
||||
|
||||
const cleanup = async () => {
|
||||
await db.delete(shows).where(inArray(shows.slug, [dune.slug]));
|
||||
await db.delete(shows).where(inArray(shows.slug, [dune.slug, "dune-2158"]));
|
||||
await db.delete(videos).where(inArray(videos.id, [duneVideo.id]));
|
||||
};
|
||||
// cleanup db beforehand to unsure tests are consistent
|
||||
|
Loading…
x
Reference in New Issue
Block a user