Move description to common types

This commit is contained in:
Zoe Roux 2025-01-25 00:15:17 +01:00
parent 9f06353f60
commit 9f929eeb7a
7 changed files with 69 additions and 69 deletions

View File

@ -16,6 +16,7 @@ import {
MovieTranslation,
} from "~/models/movie";
import {
AcceptLanguage,
Filter,
type FilterDef,
Genre,
@ -28,8 +29,8 @@ import {
processLanguages,
sortToSql,
} from "~/models/utils";
import { comment } from "~/utils";
import { db } from "../db";
import { desc } from "~/models/utils/descriptions";
const movieFilters: FilterDef = {
genres: {
@ -180,13 +181,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
}),
query: t.Object({
preferOriginal: t.Optional(
t.Boolean({
description: comment`
Prefer images in the original's language. If true, will return untranslated images instead of the translated ones.
If unspecified, kyoo will look at the current user's settings to decide what to do.
`,
}),
t.Boolean({ description: desc.preferOriginal }),
),
with: t.Array(t.UnionEnum(["translations", "videos"]), {
default: [],
@ -194,14 +189,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
}),
}),
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).
`,
}),
"accept-language": AcceptLanguage(),
}),
response: {
200: { ...FullMovie, description: "Found" },
@ -209,14 +197,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
...KError,
description: "No movie found with the given id or slug.",
},
422: {
...KError,
description: comment`
The Accept-Language header can't be satisfied (all languages listed are
unavailable.) Try with another languages or add * to the list of languages
to fallback to any language.
`,
},
422: KError,
},
},
)
@ -339,58 +320,26 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
default: ["slug"],
}),
filter: t.Optional(Filter({ def: movieFilters })),
query: t.Optional(
t.String({
description: comment`
Search query.
Searching automatically sort via relevance before the other sort parameters.
`,
}),
),
query: t.Optional(t.String({ description: desc.query })),
limit: t.Integer({
minimum: 1,
maximum: 250,
default: 50,
description: "Max page size.",
}),
after: t.Optional(
t.String({
description: comment`
Id of the cursor in the pagination.
You can ignore this and only use the prev/next field in the response.
`,
}),
),
after: t.Optional(t.String({ description: desc.after })),
preferOriginal: t.Optional(
t.Boolean({
description: comment`
Prefer images in the original's language. If true, will return untranslated images instead of the translated ones.
If unspecified, kyoo will look at the current user's settings to decide what to do.
`,
description: desc.preferOriginal,
}),
),
}),
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.)
`,
}),
"accept-language": AcceptLanguage({ autoFallback: true }),
}),
response: {
200: Page(Movie, {
description: "Paginated list of movies that match filters.",
}),
422: {
...KError,
description: "Invalid query parameters.",
},
200: Page(Movie),
422: KError,
},
},
);

View File

@ -7,6 +7,7 @@ export const KError = t.Object(
details: t.Optional(t.Any()),
},
{
description: "Invalid parameters.",
examples: [{ status: 404, message: "Movie not found" }],
},
);

View File

@ -0,0 +1,19 @@
import { comment } from "~/utils";
export const desc = {
preferOriginal: comment`
Prefer images in the original's language. If true, will return untranslated images instead of the translated ones.
If unspecified, kyoo will look at the current user's settings to decide what to do.
`,
after: comment`
Id of the cursor in the pagination.
You can ignore this and only use the prev/next field in the response.
`,
query: comment`
Search query.
Searching automatically sort via relevance before the other sort parameters.
`,
};

View File

@ -77,7 +77,13 @@ export const keysetPaginate = <
return where;
};
export const generateAfter = (cursor: any, sort: Sort<any, any>) => {
export const generateAfter = <
const ST extends NonEmptyArray<string>,
const Remap extends Partial<Record<ST[number], string>> = never,
>(
cursor: any,
sort: Sort<ST, Remap>,
) => {
const ret = [
...sort.sort.map((by) => cursor[by.remmapedKey ?? by.key]),
cursor.pk,

View File

@ -51,7 +51,7 @@ export const TranslationRecord = <T extends TSchema>(
if (!(locale.language in translations))
translations[locale.language] = translations[lang];
// normalize locale names (caps, old values etc)
// we need to do this here because the record's key (Language)'s transform is not runned.
// we need to do this here because the record's key (Language)'s transform is not run.
// this is a limitation of typebox
if (lang !== locale.baseName) {
translations[locale.baseName] = translations[lang];
@ -80,3 +80,21 @@ export const processLanguages = (languages?: string) => {
return [lang];
});
};
export const AcceptLanguage = ({
autoFallback = false,
}: { autoFallback?: boolean } = {}) =>
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).
` + autoFallback
? comment`
In this request, * is always implied (if no language could satisfy the request, kyoo will use any language available.)
`
: "",
});

View File

@ -1,7 +1,7 @@
import type { ObjectOptions } from "@sinclair/typebox";
import { type TSchema, t } from "elysia";
import { generateAfter } from "./keyset-paginate";
import type { Sort } from "./sort";
import type { NonEmptyArray, Sort } from "./sort";
export const Page = <T extends TSchema>(schema: T, options?: ObjectOptions) =>
t.Object(
@ -10,12 +10,19 @@ export const Page = <T extends TSchema>(schema: T, options?: ObjectOptions) =>
this: t.String({ format: "uri" }),
next: t.Nullable(t.String({ format: "uri" })),
},
options,
{
description: `Paginated list of ${schema.title} that match filters.`,
...options,
},
);
export const createPage = <T>(
export const createPage = <
T,
const ST extends NonEmptyArray<string>,
const Remap extends Partial<Record<ST[number], string>> = never,
>(
items: T[],
{ url, sort, limit }: { url: string; sort: Sort<any, any>; limit: number },
{ url, sort, limit }: { url: string; sort: Sort<ST, Remap>; limit: number },
) => {
let next: string | null = null;
const uri = new URL(url);

View File

@ -18,7 +18,7 @@ export type NonEmptyArray<T> = [T, ...T[]];
export const Sort = <
const T extends NonEmptyArray<string>,
const Remap extends Partial<Record<T[number], string>>,
const Remap extends Partial<Record<T[number], string>> = never,
>(
values: T,
{