Skip to content

Commit

Permalink
Move sort parsing to typebox
Browse files Browse the repository at this point in the history
  • Loading branch information
zoriya committed Jan 6, 2025
1 parent f94b845 commit bc763e5
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 26 deletions.
30 changes: 9 additions & 21 deletions api/src/controllers/movies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import {
isUuid,
processLanguages,
} from "~/models/utils";
import { comment, type RemovePrefix } from "~/utils";
import { comment } 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, MovieStatus, MovieTranslation } from "../models/movie";
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.
export function sqlarr(array: unknown[]) {
Expand Down Expand Up @@ -158,15 +159,8 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
}) => {
const langs = processLanguages(languages);
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
.select({
Expand All @@ -179,7 +173,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
.innerJoin(transQ, eq(shows.pk, transQ.pk))
.where(filter)
.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,
)
.limit(limit);
Expand All @@ -189,23 +183,17 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
{
detail: { description: "Get all movies" },
query: t.Object({
sort: t.Array(
// TODO: Add random
t.UnionEnum([
sort: Sort(
[
"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,
// TODO: Add random
remap: { airDate: "startAir" },
default: ["slug"],
description: "How to sort the query",
},
Expand Down
53 changes: 53 additions & 0 deletions api/src/models/utils/sort.ts
Original file line number Diff line number Diff line change
@@ -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");
});
5 changes: 0 additions & 5 deletions api/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,3 @@ export const comment = (str: TemplateStringsArray, ...values: any[]) =>
.replace(/^[ \t]+/gm, "") // leading spaces
.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

export type RemovePrefix<
T extends string,
Prefix extends string,
> = T extends `${Prefix}${infer Ret}` ? Ret : T;

0 comments on commit bc763e5

Please sign in to comment.