♻️ refactor: move file to related folder
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
export const getMediaReferenceAPI = (malId: number) => {
|
export const getContentReferenceAPI = (malId: number) => {
|
||||||
return {
|
return {
|
||||||
baseURL: "https://api.jikan.moe/v4",
|
baseURL: "https://api.jikan.moe/v4",
|
||||||
getMediaFullInfo: `/anime/${malId}/full`,
|
getMediaFullInfo: `/anime/${malId}/full`,
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { Context } from "elysia";
|
||||||
|
import { mainErrorHandler } from "../../../helpers/error/handler";
|
||||||
|
import { bulkInsertAnimeService } from "../services/bulkInsertAnime.service";
|
||||||
|
import { returnWriteResponse } from "../../../helpers/callback/httpResponse";
|
||||||
|
|
||||||
|
export const bulkInsertAnimeController = async (
|
||||||
|
ctx: Context & { body: { mal_id: number } },
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const bulkInsertResult = await bulkInsertAnimeService(ctx.body.mal_id);
|
||||||
|
return returnWriteResponse(
|
||||||
|
ctx.set,
|
||||||
|
201,
|
||||||
|
"Bulk insert anime operation completed successfully",
|
||||||
|
bulkInsertResult,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
return mainErrorHandler(ctx.set, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,25 +1,7 @@
|
|||||||
import Elysia, { Context } from "elysia";
|
import Elysia from "elysia";
|
||||||
import { MediaFullInfoResponse } from "./types/mediaFullInfo.type";
|
import { bulkInsertAnimeController } from "./controllers/bulkInsertAnime.controller";
|
||||||
import { InsertMediaRepository } from "./repositories/insertMedia.repository";
|
|
||||||
import { mainErrorHandler } from "../../helpers/error/handler";
|
|
||||||
|
|
||||||
const masterSourceAPI = "https://api.jikan.moe/v4";
|
|
||||||
|
|
||||||
export const internalModule = new Elysia({ prefix: "/internal" }).post(
|
export const internalModule = new Elysia({ prefix: "/internal" }).post(
|
||||||
"/medias",
|
"/media/bulk-insert",
|
||||||
async (ctx: Context & { body: { mal_id: number } }) => {
|
bulkInsertAnimeController,
|
||||||
try {
|
|
||||||
const fullMediaData = await fetch(
|
|
||||||
`${masterSourceAPI}/anime/${ctx.body.mal_id}/full`,
|
|
||||||
)
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then((data) => data as MediaFullInfoResponse);
|
|
||||||
|
|
||||||
// return fullMediaData;
|
|
||||||
const createMedia = await InsertMediaRepository(fullMediaData);
|
|
||||||
return createMedia;
|
|
||||||
} catch (error) {
|
|
||||||
return mainErrorHandler(ctx.set, error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
import { generateSlug } from "../../../helpers/characters/generateSlug";
|
||||||
|
import { AppError } from "../../../helpers/error/instances/app";
|
||||||
|
import { prisma } from "../../../utils/databases/prisma/connection";
|
||||||
|
import { MediaFullInfoResponse } from "../types/mediaFullInfo.type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Genres Insertion
|
||||||
|
*
|
||||||
|
* This section handles the insertion of genres associated with the media.
|
||||||
|
* It iterates over each genre in the media data, generates a slug for it,
|
||||||
|
* and performs an upsert operation to ensure that the genre is either created
|
||||||
|
* or updated in the database. The IDs of the inserted or updated genres are
|
||||||
|
* collected for later association with the media.
|
||||||
|
*
|
||||||
|
* @param data - The full media data containing genres information.
|
||||||
|
* @returns An array of IDs of the inserted or updated genres.
|
||||||
|
*/
|
||||||
|
export const bulkInsertGenresRepository = async (
|
||||||
|
data: MediaFullInfoResponse,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const genreIds: string[] = [];
|
||||||
|
for (const genre of data.data.genres) {
|
||||||
|
const slug = (await generateSlug(genre.name)) as string;
|
||||||
|
const genrePayload = {
|
||||||
|
name: genre.name,
|
||||||
|
malId: genre.mal_id,
|
||||||
|
malUrl: genre.url,
|
||||||
|
createdBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
||||||
|
slug,
|
||||||
|
};
|
||||||
|
const insertedGenre = await prisma.genre.upsert({
|
||||||
|
where: { slug },
|
||||||
|
create: genrePayload,
|
||||||
|
update: genrePayload,
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
genreIds.push(insertedGenre.id);
|
||||||
|
}
|
||||||
|
return genreIds;
|
||||||
|
} catch (error) {
|
||||||
|
throw new AppError(500, "Failed to insert genres", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
import { generateSlug } from "../../../helpers/characters/generateSlug";
|
||||||
|
import { AppError } from "../../../helpers/error/instances/app";
|
||||||
|
import { prisma } from "../../../utils/databases/prisma/connection";
|
||||||
|
import { MediaFullInfoResponse } from "../types/mediaFullInfo.type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Studios Insertion
|
||||||
|
*
|
||||||
|
* This section manages the insertion of studios associated with the media.
|
||||||
|
* It processes each studio listed in the media data, generating a slug for
|
||||||
|
* each and performing an upsert operation to either create or update the
|
||||||
|
* studio record in the database. The IDs of the inserted or updated studios
|
||||||
|
* are collected for later association with the media.
|
||||||
|
*
|
||||||
|
* @param data - The full media data containing studios information.
|
||||||
|
* @returns An array of IDs of the inserted or updated studios.
|
||||||
|
*/
|
||||||
|
export const bulkInsertStudiosRepository = async (
|
||||||
|
data: MediaFullInfoResponse,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const studioIds: string[] = [];
|
||||||
|
for (const studio of data.data.studios) {
|
||||||
|
const slug = (await generateSlug(studio.name)) as string;
|
||||||
|
const studioPayload = {
|
||||||
|
name: studio.name,
|
||||||
|
malId: studio.mal_id,
|
||||||
|
linkAbout: studio.url,
|
||||||
|
createdBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
||||||
|
slug,
|
||||||
|
};
|
||||||
|
const insertedStudio = await prisma.studio.upsert({
|
||||||
|
where: { slug },
|
||||||
|
create: studioPayload,
|
||||||
|
update: studioPayload,
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
studioIds.push(insertedStudio.id);
|
||||||
|
}
|
||||||
|
for (const studio of data.data.producers) {
|
||||||
|
const slug = (await generateSlug(studio.name)) as string;
|
||||||
|
const studioPayload = {
|
||||||
|
name: studio.name,
|
||||||
|
malId: studio.mal_id,
|
||||||
|
linkAbout: studio.url,
|
||||||
|
createdBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
||||||
|
slug,
|
||||||
|
};
|
||||||
|
const insertedStudio = await prisma.studio.upsert({
|
||||||
|
where: { slug },
|
||||||
|
create: studioPayload,
|
||||||
|
update: studioPayload,
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
studioIds.push(insertedStudio.id);
|
||||||
|
}
|
||||||
|
return studioIds;
|
||||||
|
} catch (error) {
|
||||||
|
throw new AppError(500, "Failed to insert studios", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { AppError } from "../../../helpers/error/instances/app";
|
||||||
|
import { prisma } from "../../../utils/databases/prisma/connection";
|
||||||
|
import { MediaFullInfoResponse } from "../types/mediaFullInfo.type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media Payload Construction and Upsert
|
||||||
|
*
|
||||||
|
* This section constructs the payload for the media insertion or update.
|
||||||
|
* It gathers all necessary information from the media data, including
|
||||||
|
* title, alternative titles, slug, associated genres and studios, score,
|
||||||
|
* images, status, airing dates, synopsis, age rating, media type, source,
|
||||||
|
* and other relevant details. This payload is then used in an upsert
|
||||||
|
* operation to ensure that the media record is either created or updated
|
||||||
|
* in the database.
|
||||||
|
*
|
||||||
|
* @param data - The full media data for constructing the media payload.
|
||||||
|
* @returns The inserted or updated media record.
|
||||||
|
*/
|
||||||
|
export const InsertMediaRepository = async ({
|
||||||
|
malId,
|
||||||
|
payload,
|
||||||
|
}: {
|
||||||
|
malId: number;
|
||||||
|
payload: Prisma.MediaUpsertArgs["create"];
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
return await prisma.media.upsert({
|
||||||
|
where: { malId },
|
||||||
|
update: payload,
|
||||||
|
create: payload,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new AppError(500, "Failed to insert media", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,134 +0,0 @@
|
|||||||
import { Prisma } from "@prisma/client";
|
|
||||||
import { generateSlug } from "../../../helpers/characters/generateSlug";
|
|
||||||
import { AppError } from "../../../helpers/error/instances/app";
|
|
||||||
import { prisma } from "../../../utils/databases/prisma/connection";
|
|
||||||
import { MediaFullInfoResponse } from "../types/mediaFullInfo.type";
|
|
||||||
|
|
||||||
export const InsertMediaRepository = async (data: MediaFullInfoResponse) => {
|
|
||||||
try {
|
|
||||||
/**
|
|
||||||
* Genres Insertion
|
|
||||||
*
|
|
||||||
* This section handles the insertion of genres associated with the media.
|
|
||||||
* It iterates over each genre in the media data, generates a slug for it,
|
|
||||||
* and performs an upsert operation to ensure that the genre is either created
|
|
||||||
* or updated in the database. The IDs of the inserted or updated genres are
|
|
||||||
* collected for later association with the media.
|
|
||||||
*
|
|
||||||
* @param data - The full media data containing genres information.
|
|
||||||
*/
|
|
||||||
const genreIds: string[] = [];
|
|
||||||
for (const genre of data.data.genres) {
|
|
||||||
const slug = (await generateSlug(genre.name)) as string;
|
|
||||||
const genrePayload = {
|
|
||||||
name: genre.name,
|
|
||||||
malId: genre.mal_id,
|
|
||||||
malUrl: genre.url,
|
|
||||||
createdBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
|
||||||
slug,
|
|
||||||
};
|
|
||||||
const insertedGenre = await prisma.genre.upsert({
|
|
||||||
where: { slug },
|
|
||||||
create: genrePayload,
|
|
||||||
update: genrePayload,
|
|
||||||
select: { id: true },
|
|
||||||
});
|
|
||||||
genreIds.push(insertedGenre.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Studios Insertion
|
|
||||||
*
|
|
||||||
* This section manages the insertion of studios associated with the media.
|
|
||||||
* It processes each studio listed in the media data, generating a slug for
|
|
||||||
* each and performing an upsert operation to either create or update the
|
|
||||||
* studio record in the database. The IDs of the inserted or updated studios
|
|
||||||
* are collected for later association with the media.
|
|
||||||
*
|
|
||||||
* @param data - The full media data containing studios information.
|
|
||||||
*/
|
|
||||||
const studioIds: string[] = [];
|
|
||||||
for (const studio of data.data.studios) {
|
|
||||||
const slug = (await generateSlug(studio.name)) as string;
|
|
||||||
const studioPayload = {
|
|
||||||
name: studio.name,
|
|
||||||
malId: studio.mal_id,
|
|
||||||
linkAbout: studio.url,
|
|
||||||
createdBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
|
||||||
slug,
|
|
||||||
};
|
|
||||||
const insertedStudio = await prisma.studio.upsert({
|
|
||||||
where: { slug },
|
|
||||||
create: studioPayload,
|
|
||||||
update: studioPayload,
|
|
||||||
select: { id: true },
|
|
||||||
});
|
|
||||||
studioIds.push(insertedStudio.id);
|
|
||||||
}
|
|
||||||
for (const studio of data.data.producers) {
|
|
||||||
const slug = (await generateSlug(studio.name)) as string;
|
|
||||||
const studioPayload = {
|
|
||||||
name: studio.name,
|
|
||||||
malId: studio.mal_id,
|
|
||||||
linkAbout: studio.url,
|
|
||||||
createdBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
|
||||||
slug,
|
|
||||||
};
|
|
||||||
const insertedStudio = await prisma.studio.upsert({
|
|
||||||
where: { slug },
|
|
||||||
create: studioPayload,
|
|
||||||
update: studioPayload,
|
|
||||||
select: { id: true },
|
|
||||||
});
|
|
||||||
studioIds.push(insertedStudio.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Media Payload Construction and Upsert
|
|
||||||
*
|
|
||||||
* This section constructs the payload for the media insertion or update.
|
|
||||||
* It gathers all necessary information from the media data, including
|
|
||||||
* title, alternative titles, slug, associated genres and studios, score,
|
|
||||||
* images, status, airing dates, synopsis, age rating, media type, source,
|
|
||||||
* and other relevant details. This payload is then used in an upsert
|
|
||||||
* operation to ensure that the media record is either created or updated
|
|
||||||
* in the database.
|
|
||||||
*
|
|
||||||
* @param data - The full media data for constructing the media payload.
|
|
||||||
*/
|
|
||||||
const construct = {
|
|
||||||
title: data.data.title,
|
|
||||||
titleAlternative: (data.data.titles as unknown) as Prisma.InputJsonValue,
|
|
||||||
slug: await generateSlug(data.data.title, {
|
|
||||||
model: "media",
|
|
||||||
target: "slug",
|
|
||||||
}),
|
|
||||||
malId: data.data.mal_id,
|
|
||||||
genres: {
|
|
||||||
connect: genreIds.map((id) => ({ id })),
|
|
||||||
},
|
|
||||||
studios: {
|
|
||||||
connect: studioIds.map((id) => ({ id })),
|
|
||||||
},
|
|
||||||
score: data.data.score,
|
|
||||||
pictureMedium: data.data.images.webp.image_url,
|
|
||||||
pictureLarge: data.data.images.webp.large_image_url,
|
|
||||||
status: data.data.status,
|
|
||||||
startAiring: data.data.aired.from,
|
|
||||||
endAiring: data.data.aired.to,
|
|
||||||
synopsis: data.data.synopsis,
|
|
||||||
ageRating: data.data.rating,
|
|
||||||
mediaType: data.data.type,
|
|
||||||
source: data.data.source,
|
|
||||||
onDraft: false,
|
|
||||||
uploadedBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
|
||||||
};
|
|
||||||
return await prisma.media.upsert({
|
|
||||||
where: { malId: data.data.mal_id },
|
|
||||||
update: construct,
|
|
||||||
create: construct,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
throw new AppError(500, "Failed to insert media", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
57
src/modules/internal/services/bulkInsertAnime.service.ts
Normal file
57
src/modules/internal/services/bulkInsertAnime.service.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { getContentReferenceAPI } from "../../../config/apis/media.reference";
|
||||||
|
import { ErrorForwarder } from "../../../helpers/error/instances/forwarder";
|
||||||
|
import { bulkInsertGenresRepository } from "../repositories/bulkInsertGenres.repository";
|
||||||
|
import { InsertMediaRepository } from "../repositories/bulkinsertMediaa.repository";
|
||||||
|
import { bulkInsertStudiosRepository } from "../repositories/bulkInsertStudios.repository";
|
||||||
|
import { MediaFullInfoResponse } from "../types/mediaFullInfo.type";
|
||||||
|
import { generateSlug } from "../../../helpers/characters/generateSlug";
|
||||||
|
|
||||||
|
export const bulkInsertAnimeService = async (malId: number) => {
|
||||||
|
try {
|
||||||
|
const { baseURL, getMediaFullInfo } = getContentReferenceAPI(malId);
|
||||||
|
const mediaFullInfo = (await fetch(baseURL + getMediaFullInfo).then((res) =>
|
||||||
|
res.json(),
|
||||||
|
)) as MediaFullInfoResponse;
|
||||||
|
|
||||||
|
const insertedGenres = await bulkInsertGenresRepository(mediaFullInfo);
|
||||||
|
const insertedStudios = await bulkInsertStudiosRepository(mediaFullInfo);
|
||||||
|
|
||||||
|
const constructMediaPayload = {
|
||||||
|
title: mediaFullInfo.data.title,
|
||||||
|
titleAlternative: (mediaFullInfo.data
|
||||||
|
.titles as unknown) as Prisma.InputJsonValue,
|
||||||
|
slug: await generateSlug(mediaFullInfo.data.title, {
|
||||||
|
model: "media",
|
||||||
|
target: "slug",
|
||||||
|
}),
|
||||||
|
malId: mediaFullInfo.data.mal_id,
|
||||||
|
genres: {
|
||||||
|
connect: insertedGenres.map((id) => ({ id })),
|
||||||
|
},
|
||||||
|
studios: {
|
||||||
|
connect: insertedStudios.map((id) => ({ id })),
|
||||||
|
},
|
||||||
|
score: mediaFullInfo.data.score,
|
||||||
|
pictureMedium: mediaFullInfo.data.images.webp.image_url,
|
||||||
|
pictureLarge: mediaFullInfo.data.images.webp.large_image_url,
|
||||||
|
status: mediaFullInfo.data.status,
|
||||||
|
startAiring: mediaFullInfo.data.aired.from,
|
||||||
|
endAiring: mediaFullInfo.data.aired.to,
|
||||||
|
synopsis: mediaFullInfo.data.synopsis,
|
||||||
|
ageRating: mediaFullInfo.data.rating,
|
||||||
|
mediaType: mediaFullInfo.data.type,
|
||||||
|
source: mediaFullInfo.data.source,
|
||||||
|
onDraft: false,
|
||||||
|
uploadedBy: "b734b9bc-b4ea-408f-a80e-0a837ce884da",
|
||||||
|
};
|
||||||
|
const insertedMedia = await InsertMediaRepository({
|
||||||
|
malId: mediaFullInfo.data.mal_id,
|
||||||
|
payload: constructMediaPayload,
|
||||||
|
});
|
||||||
|
|
||||||
|
return insertedMedia;
|
||||||
|
} catch (error) {
|
||||||
|
ErrorForwarder(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user