From 856f6809e68b3782036f4c19da0447ccdd461321 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Sun, 14 Apr 2024 00:27:25 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9A=20Added=20volume-related=20endpoin?= =?UTF-8?q?ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/comicvine.service.ts | 209 +++++++++++++++++++++++++++++++--- 1 file changed, 194 insertions(+), 15 deletions(-) diff --git a/services/comicvine.service.ts b/services/comicvine.service.ts index ef4b07d..d645b69 100644 --- a/services/comicvine.service.ts +++ b/services/comicvine.service.ts @@ -3,7 +3,7 @@ import { Service, ServiceBroker, Context } from "moleculer"; import axios from "axios"; import delay from "delay"; -import { isNil } from "lodash"; +import { isNil, isUndefined } from "lodash"; import { fetchReleases, FilterTypes, SortTypes } from "comicgeeks"; import { matchScorer, rankVolumes } from "../utils/searchmatchscorer.utils"; import { @@ -11,6 +11,7 @@ import { scrapeIssuePage, } from "../utils/scraping.utils"; const { calculateLimitAndOffset, paginate } = require("paginate-info"); +const { MoleculerError } = require("moleculer").Errors; const CV_BASE_URL = "https://comicvine.gamespot.com/api/"; console.log("ComicVine API Key: ", process.env.COMICVINE_API_KEY); @@ -28,7 +29,7 @@ export default class ComicVineService extends Service { format: string; sort: string; query: string; - fieldList: string; + field_list: string; limit: string; offset: string; resources: string; @@ -53,18 +54,23 @@ export default class ComicVineService extends Service { handler: async ( ctx: Context<{ volumeURI: string; - data: {}; + fieldList: string; }> ) => { + const { volumeURI, fieldList } = ctx.params; const response = await axios.request({ url: - ctx.params.volumeURI + + volumeURI + "?api_key=" + process.env.COMICVINE_API_KEY, params: { format: "json", + field_list: fieldList, + }, + headers: { + Accept: "application/json", + "User-Agent": "ThreeTwo", }, - headers: { Accept: "application/json" }, }); const { data } = response; return data; @@ -161,30 +167,45 @@ export default class ComicVineService extends Service { return { result: paginatedData, meta: paginationInfo }; }, }, - getStoryArcs: { - rest: "POST /getStoryArcs", - handler: async (ctx: Context<{ comicObject: any }>) => { - const { comicObject } = ctx.params; - console.log(JSON.stringify(comicObject, null, 2)); + getResource: { + rest: "POST /getResource", + handler: async ( + ctx: Context<{ + resources: string; + filter: string; + fieldList: string; + }> + ) => { + const { resources, filter, fieldList } = ctx.params; + console.log(JSON.stringify(ctx.params, null, 2)); + console.log( + CV_BASE_URL + + `${resources}` + + "?api_key=" + + process.env.COMICVINE_API_KEY + ); // 2. Query CV and get metadata for them - const storyArcs = await axios({ + const response = await axios({ + method: "GET", url: CV_BASE_URL + - "story_arcs" + + `${resources}` + "?api_key=" + process.env.COMICVINE_API_KEY, params: { - resources: "story_arcs", + resources: `${resources}`, limit: "100", format: "json", - filter: `volume:${comicObject.sourcedMetadata.comicvine.volumeInformation.id}`, + filter: `${filter}`, + field_list: `${fieldList}`, }, headers: { Accept: "application/json", "User-Agent": "ThreeTwo", }, }); - return storyArcs.data; + console.log(response.data); + return response.data; }, }, volumeBasedSearch: { @@ -356,6 +377,164 @@ export default class ComicVineService extends Service { ); }, }, + getStoryArcs: { + rest: "POST /getStoryArcs", + handler: async ( + ctx: Context<{ volumeUrl: string; volumeId: number }> + ) => { + const { volumeUrl, volumeId } = ctx.params; + try { + const volumeResponse = await axios({ + url: + volumeUrl + + "?api_key=" + + process.env.COMICVINE_API_KEY, + method: "GET", + params: { + limit: "100", + format: "json", + resources: "volumes", + }, + headers: { + Accept: "application/json", + "User-Agent": "ThreeTwo", + }, + }); + const volumeData = volumeResponse.data; + + if (volumeData.results.issues.length > 0) { + const issuePromises = + volumeData.results.issues.map( + async (issue: any) => { + const issueUrl = `${CV_BASE_URL}issue/4000-${issue.id}/?api_key=${process.env.COMICVINE_API_KEY}&format=json&field_list=story_arc_credits,description,image`; + try { + const issueResponse = + await axios.get(issueUrl, { + params: { + limit: "100", + format: "json", + }, + headers: { + Accept: "application/json", + "User-Agent": + "ThreeTwo", + }, + }); + const issueData = + issueResponse.data.results; + + // Transform each story arc to include issue's description and image + return ( + issueData.story_arc_credits?.map( + (arc: any) => ({ + ...arc, + issueDescription: + issueData.description, + issueImage: + issueData.image, + }) + ) || [] + ); + } catch (error) { + console.error( + "An error occurred while fetching issue data:", + error.message + ); + return []; // Return an empty array on error + } + } + ); + + try { + const storyArcsResults: any = + await Promise.all(issuePromises); + // Flatten the array of arrays + const flattenedStoryArcs = + storyArcsResults.flat(); + + // Deduplicate based on arc ID, while preserving the last seen issueDescription and issueImage + const uniqueStoryArcs = Array.from( + new Map( + flattenedStoryArcs.map( + (arc: any) => [arc.id, arc] + ) + ).values() + ); + + console.log( + `Found ${uniqueStoryArcs.length} unique story arc(s) for volume ID ${volumeId}:` + ); + uniqueStoryArcs.forEach((arc: any) => { + console.log( + `- ${arc.name} (ID: ${arc.id}) with issueDescription and issueImage` + ); + }); + + return uniqueStoryArcs; + } catch (error) { + console.error( + "An error occurred while processing story arcs:", + error + ); + } + } else { + console.log( + "No issues found for the specified volume." + ); + } + } catch (error) { + console.error( + "An error occurred while fetching data from ComicVine:", + error + ); + } + }, + }, + + getIssuesForVolume: { + rest: "POST /getIssuesForVolume", + + async handler(ctx: Context<{ volumeId: number }>) { + const { volumeId } = ctx.params; + const issuesUrl = `${CV_BASE_URL}issues/?api_key=${process.env.COMICVINE_API_KEY}&filter=volume:${volumeId}&format=json&field_list=id,name,issue_number,description,image`; + + try { + const response = await axios({ + url: issuesUrl, + method: "GET", + params: { + limit: "100", + format: "json", + }, + headers: { + Accept: "application/json", + "User-Agent": "ThreeTwo", + }, + }); + + // Map over the issues to ensure each issue has `description` and `image` fields + const issuesWithDescriptionAndImage = + response.data.results.map((issue: any) => ({ + ...issue, + description: issue.description || "", // Ensure description field exists and default to empty string if not + image: issue.image || {}, // Ensure image field exists and default to empty object if not + })); + + return issuesWithDescriptionAndImage; + } catch (error) { + this.logger.error( + "Error fetching issues from ComicVine:", + error.message + ); + throw new MoleculerError( + "Failed to fetch issues", + 500, + "FETCH_ERROR", + { error: error.message } + ); + } + }, + }, }, methods: { fetchVolumesFromCV: async (payload, output: any[] = []) => {