diff --git a/prisma/dbml/schema.dbml b/prisma/dbml/schema.dbml index b8dd824..5853552 100644 --- a/prisma/dbml/schema.dbml +++ b/prisma/dbml/schema.dbml @@ -181,6 +181,10 @@ Table videos { deletedAt DateTime createdAt DateTime [default: `now()`, not null] updatedAt DateTime [default: `now()`, not null] + + indexes { + (serviceId, code) [unique] + } } Table video_services { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9d5307b..0c255ec 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -218,6 +218,8 @@ model Video { deletedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt + + @@unique([serviceId, code]) @@map("videos") } diff --git a/src/modules/internal/controllers/bulkInsertVideo.controller.ts b/src/modules/internal/controllers/bulkInsertVideo.controller.ts new file mode 100644 index 0000000..d68450a --- /dev/null +++ b/src/modules/internal/controllers/bulkInsertVideo.controller.ts @@ -0,0 +1,26 @@ +import { Context } from "elysia"; +import { mainErrorHandler } from "../../../helpers/error/handler"; +import { bulkInsertVideoService } from "../services/http/bulkInsertVideo.service"; +import { returnWriteResponse } from "../../../helpers/callback/httpResponse"; + +export interface BulkInsertVideoBodyRequest { + media_id: string; + data: Array<{ + episode: number; + videos: Array<{ + service_id: string; + code: string; + }>; + }>; +} + +export const bulkInsertVideoController = async ( + ctx: Context & { body: BulkInsertVideoBodyRequest }, +) => { + try { + const insertedVideos = await bulkInsertVideoService(ctx.body); + return returnWriteResponse(ctx.set, 201, "Videos inserted", insertedVideos); + } catch (error) { + throw mainErrorHandler(ctx.set, error); + } +}; diff --git a/src/modules/internal/index.ts b/src/modules/internal/index.ts index 41c539f..471c1c9 100644 --- a/src/modules/internal/index.ts +++ b/src/modules/internal/index.ts @@ -2,8 +2,10 @@ import Elysia from "elysia"; import { bulkInsertEpisodeController } from "./controllers/bulkInsertEpisode.controller"; import { bulkInsertMediaController } from "./controllers/bulkInsertMedia.controller"; import { createVideoServiceInternalController } from "./controllers/createVideoService.controller"; +import { bulkInsertVideoController } from "./controllers/bulkInsertVideo.controller"; export const internalModule = new Elysia({ prefix: "/internal" }) .post("/media/bulk-insert", bulkInsertMediaController) .post("/episode/bulk-insert", bulkInsertEpisodeController) + .post("/video/bulk-insert", bulkInsertVideoController) .post("/video-service", createVideoServiceInternalController); diff --git a/src/modules/internal/repositories/bulkInsertVideo.repository.ts b/src/modules/internal/repositories/bulkInsertVideo.repository.ts new file mode 100644 index 0000000..21eb3cf --- /dev/null +++ b/src/modules/internal/repositories/bulkInsertVideo.repository.ts @@ -0,0 +1,26 @@ +import { Prisma } from "@prisma/client"; +import { AppError } from "../../../helpers/error/instances/app"; +import { prisma } from "../../../utils/databases/prisma/connection"; +import { generateUUIDv7 } from "../../../helpers/databases/uuidv7"; + +export const bulkInsertVideoRepository = async ( + payload: Omit, +) => { + try { + return await prisma.video.upsert({ + where: { + serviceId_code: { + serviceId: payload.serviceId, + code: payload.code, + }, + }, + create: { + id: generateUUIDv7(), + ...payload, + }, + update: payload, + }); + } catch (error) { + throw new AppError(500, "Error inserting video", error); + } +}; diff --git a/src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts b/src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts new file mode 100644 index 0000000..0817e06 --- /dev/null +++ b/src/modules/internal/repositories/findEpisodeWithMediaId.repository.ts @@ -0,0 +1,28 @@ +import { AppError } from "../../../helpers/error/instances/app"; +import { prisma } from "../../../utils/databases/prisma/connection"; + +export const findEpisodeWithMediaIdRepository = async ({ + media, + episode, +}: { + media: string; + episode: number; +}) => { + try { + const foundEpisode = await prisma.episode.findUnique({ + where: { + mediaId_episode: { + mediaId: media, + episode: episode, + }, + }, + select: { + id: true, + }, + }); + if (!foundEpisode) throw new AppError(404, "Episode not found"); + return foundEpisode; + } catch (error) { + throw new AppError(500, "Error finding episode with media id", error); + } +}; diff --git a/src/modules/internal/services/http/bulkInsertVideo.service.ts b/src/modules/internal/services/http/bulkInsertVideo.service.ts new file mode 100644 index 0000000..d10fa5b --- /dev/null +++ b/src/modules/internal/services/http/bulkInsertVideo.service.ts @@ -0,0 +1,34 @@ +import { SystemAccountId } from "../../../../config/account/system"; +import { ErrorForwarder } from "../../../../helpers/error/instances/forwarder"; +import { BulkInsertVideoBodyRequest } from "../../controllers/bulkInsertVideo.controller"; +import { findEpisodeWithMediaIdRepository } from "../../repositories/findEpisodeWithMediaId.repository"; +import { bulkInsertVideoRepository } from "../../repositories/bulkInsertVideo.repository"; + +export const bulkInsertVideoService = async ( + body: BulkInsertVideoBodyRequest, +) => { + try { + const insertedVideos: string[] = []; + for (const episodeData of body.data) { + const episodeId = await findEpisodeWithMediaIdRepository({ + media: body.media_id, + episode: episodeData.episode, + }); + + for (const videoData of episodeData.videos) { + const insertedVideo = await bulkInsertVideoRepository({ + episodeId: episodeId.id, + serviceId: videoData.service_id, + code: videoData.code, + uploadedBy: SystemAccountId, + }); + + insertedVideos.push(insertedVideo.id); + } + } + + return insertedVideos; + } catch (error) { + ErrorForwarder(error); + } +};