mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-31 04:04:21 -04:00
Make movie get work
This commit is contained in:
parent
9e1afca9ec
commit
eea0f688a0
@ -11,6 +11,7 @@ export const base = new Elysia({ name: "base" })
|
|||||||
} as KError;
|
} as KError;
|
||||||
}
|
}
|
||||||
if (code === "INTERNAL_SERVER_ERROR") {
|
if (code === "INTERNAL_SERVER_ERROR") {
|
||||||
|
console.error(error);
|
||||||
return {
|
return {
|
||||||
status: 500,
|
status: 500,
|
||||||
message: error.message,
|
message: error.message,
|
||||||
|
@ -1,57 +1,45 @@
|
|||||||
|
import { and, eq, sql } from "drizzle-orm";
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import { Movie, MovieTranslation } from "../models/movie";
|
import { KError } from "~/models/error";
|
||||||
|
import { isUuid, processLanguages } from "~/models/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 { eq, and, sql, or } from "drizzle-orm";
|
|
||||||
import { getColumns } from "../db/schema/utils";
|
import { getColumns } from "../db/schema/utils";
|
||||||
import { bubble } from "../models/examples";
|
import { bubble } from "../models/examples";
|
||||||
import { comment } from "~/utils";
|
import { Movie, MovieTranslation } from "../models/movie";
|
||||||
import { processLanguages } from "~/models/utils";
|
|
||||||
|
|
||||||
const translations = db
|
// drizzle is bugged and doesn't allow js arrays to be used in raw sql.
|
||||||
.selectDistinctOn([showTranslations.pk])
|
export function sqlarr(array: unknown[]) {
|
||||||
.from(showTranslations)
|
return `{${array.map((item) => `"${item}"`).join(",")}}`;
|
||||||
// .where(
|
}
|
||||||
// or(
|
|
||||||
// eq(showTranslations.language, sql`any(${sql.placeholder("langs")})`),
|
const getTranslationQuery = (languages: string[]) => {
|
||||||
// eq(showTranslations.language, shows.originalLanguage),
|
const fallback = languages.includes("*");
|
||||||
// ),
|
const query = db
|
||||||
// )
|
.selectDistinctOn([showTranslations.pk])
|
||||||
.orderBy(
|
.from(showTranslations)
|
||||||
showTranslations.pk,
|
.where(
|
||||||
sql`array_position(${sql.placeholder("langs")}, ${showTranslations.language})`,
|
fallback
|
||||||
)
|
? undefined
|
||||||
.as("t");
|
: eq(showTranslations.language, sql`any(${sqlarr(languages)})`),
|
||||||
|
)
|
||||||
|
.orderBy(
|
||||||
|
showTranslations.pk,
|
||||||
|
sql`array_position(${sqlarr(languages)}, ${showTranslations.language})`,
|
||||||
|
)
|
||||||
|
.as("t");
|
||||||
|
|
||||||
|
const { pk, ...col } = getColumns(query);
|
||||||
|
return [query, col] as const;
|
||||||
|
};
|
||||||
|
|
||||||
const { pk: _, kind, startAir, endAir, ...moviesCol } = getColumns(shows);
|
const { pk: _, kind, startAir, endAir, ...moviesCol } = getColumns(shows);
|
||||||
const { pk, language, ...translationsCol } = getColumns(translations);
|
|
||||||
|
|
||||||
const findMovie = db
|
|
||||||
.select({
|
|
||||||
...moviesCol,
|
|
||||||
...translationsCol,
|
|
||||||
airDate: startAir,
|
|
||||||
})
|
|
||||||
.from(shows)
|
|
||||||
.innerJoin(translations, eq(shows.pk, translations.pk))
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(shows.kind, "movie"),
|
|
||||||
// or(
|
|
||||||
// eq(shows.id, sql.placeholder("id")),
|
|
||||||
eq(shows.slug, sql.placeholder("id")),
|
|
||||||
// ),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
// .orderBy()
|
|
||||||
.limit(1)
|
|
||||||
.prepare("findMovie");
|
|
||||||
|
|
||||||
export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
|
export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
|
||||||
.model({
|
.model({
|
||||||
movie: Movie,
|
movie: Movie,
|
||||||
"movie-translation": MovieTranslation,
|
"movie-translation": MovieTranslation,
|
||||||
error: t.Object({}),
|
|
||||||
})
|
})
|
||||||
.guard({
|
.guard({
|
||||||
params: t.Object({
|
params: t.Object({
|
||||||
@ -61,7 +49,7 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
headers: t.Object({
|
headers: t.Object({
|
||||||
"Accept-Language": t.String({
|
"accept-language": t.String({
|
||||||
default: "*",
|
default: "*",
|
||||||
examples: "en-us, ja;q=0.5",
|
examples: "en-us, ja;q=0.5",
|
||||||
description: comment`
|
description: comment`
|
||||||
@ -71,26 +59,66 @@ export const movies = new Elysia({ prefix: "/movies", tags: ["movies"] })
|
|||||||
`,
|
`,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
response: { 200: "movie", 404: "error" },
|
response: {
|
||||||
|
200: "movie",
|
||||||
|
404: {
|
||||||
|
...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.
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.get(
|
.get(
|
||||||
"/:id",
|
"/:id",
|
||||||
async ({
|
async ({
|
||||||
params: { id },
|
params: { id },
|
||||||
headers: { "Accept-Language": languages },
|
headers: { "accept-language": languages },
|
||||||
error,
|
error,
|
||||||
|
set,
|
||||||
}) => {
|
}) => {
|
||||||
const langs = processLanguages(languages);
|
const langs = processLanguages(languages);
|
||||||
console.log(langs);
|
const [transQ, transCol] = getTranslationQuery(langs);
|
||||||
console.log(findMovie.getQuery());
|
|
||||||
const ret = await findMovie.execute({ id, langs });
|
const idFilter = isUuid(id) ? eq(shows.id, id) : eq(shows.slug, id);
|
||||||
console.log(ret);
|
|
||||||
if (ret.length !== 1) return error(404, {});
|
const [ret] = await db
|
||||||
return ret[0];
|
.select({
|
||||||
|
...moviesCol,
|
||||||
|
...transCol,
|
||||||
|
airDate: startAir,
|
||||||
|
})
|
||||||
|
.from(shows)
|
||||||
|
.leftJoin(transQ, eq(shows.pk, transQ.pk))
|
||||||
|
.where(and(eq(shows.kind, "movie"), idFilter))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
return error(404, {
|
||||||
|
status: 404,
|
||||||
|
message: "Movie not found",
|
||||||
|
details: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!ret.language) {
|
||||||
|
return error(422, {
|
||||||
|
status: 422,
|
||||||
|
message: "Accept-Language header could not be satisfied.",
|
||||||
|
details: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
set.headers["content-language"] = ret.language;
|
||||||
|
return ret;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
detail: {
|
detail: {
|
||||||
description: "Get a movie by id or slug",
|
description: "Get a movie by id or slug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { FormatRegistry } from "@sinclair/typebox";
|
import { FormatRegistry } from "@sinclair/typebox";
|
||||||
|
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
||||||
import { t } from "elysia";
|
import { t } from "elysia";
|
||||||
|
|
||||||
export const slugPattern = "^[a-z0-9-]+$";
|
export const slugPattern = "^[a-z0-9-]+$";
|
||||||
@ -11,3 +12,6 @@ export const Resource = t.Object({
|
|||||||
id: t.String({ format: "uuid" }),
|
id: t.String({ format: "uuid" }),
|
||||||
slug: t.String({ format: "slug" }),
|
slug: t.String({ format: "slug" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const checker = TypeCompiler.Compile(t.String({ format: "uuid" }));
|
||||||
|
export const isUuid = (id: string) => checker.Check(id);
|
||||||
|
@ -22,6 +22,8 @@ const getMovie = async (id: string, langs: string) => {
|
|||||||
return [resp, body] as const;
|
return [resp, body] as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let bubbleId = "";
|
||||||
|
|
||||||
function expectStatus(resp: Response, body: object) {
|
function expectStatus(resp: Response, body: object) {
|
||||||
const matcher = expect({ ...body, status: resp.status });
|
const matcher = expect({ ...body, status: resp.status });
|
||||||
return {
|
return {
|
||||||
@ -41,12 +43,50 @@ describe("Get movie", () => {
|
|||||||
name: bubble.translations.en.name,
|
name: bubble.translations.en.name,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it("Retrive by id", async () => {
|
||||||
|
const [resp, body] = await getMovie(bubbleId, "en");
|
||||||
|
|
||||||
|
expectStatus(resp, body).toBe(200);
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
id: bubbleId,
|
||||||
|
slug: bubble.slug,
|
||||||
|
name: bubble.translations.en.name,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Get non available translation", async () => {
|
||||||
|
const [resp, body] = await getMovie(bubble.slug, "fr");
|
||||||
|
|
||||||
|
expectStatus(resp, body).toBe(422);
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
status: 422,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Get first available language", async () => {
|
||||||
|
const [resp, body] = await getMovie(bubble.slug, "fr,en");
|
||||||
|
|
||||||
|
expectStatus(resp, body).toBe(200);
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
slug: bubble.slug,
|
||||||
|
name: bubble.translations.en.name,
|
||||||
|
});
|
||||||
|
expect(resp.headers.get("Content-Language")).toBe("en");
|
||||||
|
});
|
||||||
|
it("Use language fallback", async () => {
|
||||||
|
const [resp, body] = await getMovie(bubble.slug, "fr,ja,*");
|
||||||
|
|
||||||
|
expectStatus(resp, body).toBe(200);
|
||||||
|
expect(body).toMatchObject({
|
||||||
|
slug: bubble.slug,
|
||||||
|
name: bubble.translations.en.name,
|
||||||
|
});
|
||||||
|
expect(resp.headers.get("Content-Language")).toBe("en");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const ret = await seedMovie(bubble);
|
const ret = await seedMovie(bubble);
|
||||||
console.log("seed bubble", ret);
|
bubbleId = ret.id;
|
||||||
});
|
});
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
// await db.delete(shows).where(eq(shows.slug, bubble.slug));
|
await db.delete(shows).where(eq(shows.slug, bubble.slug));
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user