Move sort parsing to typebox

This commit is contained in:
Zoe Roux 2025-01-06 18:30:48 +01:00
parent 0499be4194
commit 482ad0dda2
No known key found for this signature in database
3 changed files with 62 additions and 26 deletions

View File

@ -7,13 +7,14 @@ import {
isUuid, isUuid,
processLanguages, processLanguages,
} from "~/models/utils"; } from "~/models/utils";
import { comment, type RemovePrefix } from "~/utils"; import { comment } from "~/utils";
import { db } from "../db"; import { db } from "../db";
import { shows, showTranslations } from "../db/schema/shows"; import { shows, showTranslations } from "../db/schema/shows";
import { getColumns } from "../db/schema/utils"; import { getColumns } from "../db/schema/utils";
import { bubble } from "../models/examples"; import { bubble } from "../models/examples";
import { Movie, MovieStatus, MovieTranslation } from "../models/movie"; import { Movie, MovieStatus, MovieTranslation } from "../models/movie";
import { Filter, type Page } from "~/models/utils"; import { Filter, type Page } from "~/models/utils";
import { Sort } from "~/models/utils/sort";
// drizzle is bugged and doesn't allow js arrays to be used in raw sql. // drizzle is bugged and doesn't allow js arrays to be used in raw sql.
export function sqlarr(array: unknown[]) { export function sqlarr(array: unknown[]) {
@ -158,15 +159,8 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
}) => { }) => {
const langs = processLanguages(languages); const langs = processLanguages(languages);
const [transQ, transCol] = getTranslationQuery(langs); const [transQ, transCol] = getTranslationQuery(langs);
// TODO: move this to typebox transform
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 };
});
// TODO: Add sql indexes on order keys // TODO: Add sql indexes on sort keys
const items = await db const items = await db
.select({ .select({
@ -179,7 +173,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
.innerJoin(transQ, eq(shows.pk, transQ.pk)) .innerJoin(transQ, eq(shows.pk, transQ.pk))
.where(filter) .where(filter)
.orderBy( .orderBy(
...order.map((x) => (x.desc ? desc(shows[x.key]) : shows[x.key])), ...sort.map((x) => (x.desc ? desc(shows[x.key]) : shows[x.key])),
shows.pk, shows.pk,
) )
.limit(limit); .limit(limit);
@ -189,23 +183,17 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
{ {
detail: { description: "Get all movies" }, detail: { description: "Get all movies" },
query: t.Object({ query: t.Object({
sort: t.Array( sort: Sort(
// TODO: Add random [
t.UnionEnum([
"slug", "slug",
"-slug",
"rating", "rating",
"-rating",
"airDate", "airDate",
"-airDate",
"createdAt", "createdAt",
"-createdAt",
"nextRefresh", "nextRefresh",
"-nextRefresh", ],
]),
// TODO: support explode: true (allow sort=slug,-createdAt). needs a pr to elysia
{ {
explode: false, // TODO: Add random
remap: { airDate: "startAir" },
default: ["slug"], default: ["slug"],
description: "How to sort the query", description: "How to sort the query",
}, },

View File

@ -0,0 +1,53 @@
import { t } from "elysia";
type Sort<
T extends string[],
Remap extends Partial<Record<T[number], string>>,
> = {
key: Exclude<T[number], keyof Remap> | Remap[keyof Remap];
desc: boolean;
}[];
type NonEmptyArray<T> = [T, ...T[]];
export const Sort = <
const T extends NonEmptyArray<string>,
const Remap extends Partial<Record<T[number], string>>,
>(
values: T,
{
description = "How to sort the query",
default: def,
remap,
}: {
default?: T[number][];
description: string;
remap: Remap;
},
) =>
t
.Transform(
t.Array(
t.UnionEnum([
...values,
...values.map((x: T[number]) => `-${x}` as const),
]),
{
// TODO: support explode: true (allow sort=slug,-createdAt). needs a pr to elysia
explode: false,
default: def,
description: description,
},
),
)
.Decode((sort): Sort<T, Remap> => {
return sort.map((x) => {
const desc = x[0] === "-";
const key = (desc ? x.substring(1) : x) as T[number];
if (key in remap) return { key: remap[key], desc };
return { key: key as Exclude<typeof key, keyof Remap>, desc };
});
})
.Encode(() => {
throw new Error("Encode not supported for sort");
});

View File

@ -6,8 +6,3 @@ export const comment = (str: TemplateStringsArray, ...values: any[]) =>
.replace(/^[ \t]+/gm, "") // leading spaces .replace(/^[ \t]+/gm, "") // leading spaces
.replace(/([^\n])\n([^\n])/g, "$1 $2") // two lines to space separated line .replace(/([^\n])\n([^\n])/g, "$1 $2") // two lines to space separated line
.replace(/\n{2}/g, "\n"); // keep newline if there's an empty line .replace(/\n{2}/g, "\n"); // keep newline if there's an empty line
export type RemovePrefix<
T extends string,
Prefix extends string,
> = T extends `${Prefix}${infer Ret}` ? Ret : T;