Add get /movies & sort api

This commit is contained in:
Zoe Roux 2024-12-19 16:21:12 +01:00
parent a4853cb186
commit 587dc4f970
No known key found for this signature in database
3 changed files with 119 additions and 2 deletions

View File

@ -1,13 +1,14 @@
import { and, eq, sql } from "drizzle-orm";
import { and, desc, eq, sql } from "drizzle-orm";
import { Elysia, t } from "elysia";
import { KError } from "~/models/error";
import { isUuid, processLanguages } from "~/models/utils";
import { comment } from "~/utils";
import { comment, RemovePrefix } from "~/utils";
import { db } from "../db";
import { shows, showTranslations } from "../db/schema/shows";
import { getColumns } from "../db/schema/utils";
import { bubble } from "../models/examples";
import { Movie, type MovieStatus, MovieTranslation } from "../models/movie";
import { Page } from "~/models/utils/page";
// drizzle is bugged and doesn't allow js arrays to be used in raw sql.
export function sqlarr(array: unknown[]) {
@ -130,4 +131,102 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
},
},
},
)
.get(
"",
async ({
query: { limit, after, sort },
headers: { "accept-language": languages },
}) => {
const langs = processLanguages(languages);
const [transQ, transCol] = getTranslationQuery(langs);
const order = sort.map((x) => {
const desc = x[0] === "-";
const key = (desc ? x.substring(1) : x) as RemovePrefix<typeof x, "-">;
if (key === "airDate") return { key: "startAir" as const, desc };
return { key, desc };
});
const items = await db
.select({
...moviesCol,
...transCol,
status: sql<MovieStatus>`${moviesCol.status}`,
airDate: startAir,
})
.from(shows)
.innerJoin(transQ, eq(shows.pk, transQ.pk))
.orderBy(
...order.map((x) => (x.desc ? desc(shows[x.key]) : shows[x.key])),
shows.pk,
)
.limit(limit);
return { items, next: "", prev: "", this: "" };
},
{
detail: { description: "Get all movies" },
query: t.Object({
sort: t.Array(
t.UnionEnum([
"slug",
"-slug",
"rating",
"-rating",
"airDate",
"-airDate",
"createdAt",
"-createdAt",
"nextRefresh",
"-nextRefresh",
]),
// TODO: support explode: true (allow sort=slug,-createdAt). needs a pr to elysia
{ explode: false, default: ["slug"] },
),
limit: t.Integer({
minimum: 1,
maximum: 250,
default: 50,
description: "Max page size.",
}),
after: t.Optional(
t.String({
format: "uuid",
description: comment`
Id of the cursor in the pagination.
You can ignore this and only use the prev/next field in the response.
`,
}),
),
}),
headers: t.Object({
"accept-language": t.String({
default: "*",
example: "en-us, ja;q=0.5",
description: comment`
List of languages you want the data in.
This follows the Accept-Language offical specification
(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language).
In this request, * is always implied (if no language could satisfy the request, kyoo will use any language available).
`,
}),
}),
// response: {
// 200: Page(Movie, {
// description: "Paginated list of movies that match filters.",
// }),
// 422: {
// ...KError,
// description: "Invalid query parameters.",
// examples: [
// {
// status: 422,
// message: "Accept-Language header could not be satisfied.",
// details: undefined,
// },
// ],
// },
// },
},
);

View File

@ -0,0 +1,13 @@
import type { ObjectOptions } from "@sinclair/typebox";
import { t, type TSchema } from "elysia";
export const Page = <T extends TSchema>(schema: T, options?: ObjectOptions) =>
t.Object(
{
items: t.Array(schema),
this: t.String({ format: "uri" }),
prev: t.String({ format: "uri" }),
next: t.String({ format: "uri" }),
},
options,
);

View File

@ -1,3 +1,8 @@
// remove indent in multi-line comments
export const comment = (str: TemplateStringsArray, ...values: any[]) =>
str.reduce((acc, str, i) => `${acc}${str}${values[i]}`).replace(/^\s+/gm, "");
export type RemovePrefix<
T extends string,
Prefix extends string,
> = T extends `${Prefix}${infer Ret}` ? Ret : T;