🪢Added resolvers for lib, dashboard endpoints

This commit is contained in:
2026-03-04 21:49:38 -05:00
parent 8c224bad68
commit 22cbdcd468
9 changed files with 1846 additions and 10 deletions

View File

@@ -27,6 +27,202 @@ export const resolvers = {
}
},
/**
* Get comic books with advanced pagination and filtering
*/
getComicBooks: async (
_: any,
{
paginationOptions,
predicate = {},
}: {
paginationOptions: any;
predicate?: any;
}
) => {
try {
const result = await Comic.paginate(predicate, paginationOptions);
return result;
} catch (error) {
console.error("Error fetching comic books:", error);
throw new Error("Failed to fetch comic books");
}
},
/**
* Get comic book groups (volumes with multiple issues)
*/
getComicBookGroups: async () => {
try {
const volumes = await Comic.aggregate([
{
$project: {
volumeInfo:
"$sourcedMetadata.comicvine.volumeInformation",
},
},
{
$unwind: "$volumeInfo",
},
{
$group: {
_id: "$_id",
volumes: {
$addToSet: "$volumeInfo",
},
},
},
{
$unwind: "$volumes",
},
{ $sort: { updatedAt: -1 } },
{ $skip: 0 },
{ $limit: 5 },
]);
return volumes.map((vol) => ({
id: vol._id.toString(),
volumes: vol.volumes,
}));
} catch (error) {
console.error("Error fetching comic book groups:", error);
throw new Error("Failed to fetch comic book groups");
}
},
/**
* Get library statistics
*/
getLibraryStatistics: async () => {
try {
const { getSizeOfDirectory } = require("../../utils/file.utils");
const { COMICS_DIRECTORY } = require("../../constants/directories");
const comicDirectorySize = await getSizeOfDirectory(
COMICS_DIRECTORY,
[".cbz", ".cbr", ".cb7"]
);
const totalCount = await Comic.countDocuments({});
const statistics = await Comic.aggregate([
{
$facet: {
fileTypes: [
{
$match: {
"rawFileDetails.extension": {
$in: [".cbr", ".cbz", ".cb7"],
},
},
},
{
$group: {
_id: "$rawFileDetails.extension",
data: { $push: "$$ROOT._id" },
},
},
],
issues: [
{
$match: {
"sourcedMetadata.comicvine.volumeInformation":
{
$gt: {},
},
},
},
{
$group: {
_id: "$sourcedMetadata.comicvine.volumeInformation",
data: { $push: "$$ROOT._id" },
},
},
],
fileLessComics: [
{
$match: {
rawFileDetails: {
$exists: false,
},
},
},
],
issuesWithComicInfoXML: [
{
$match: {
"sourcedMetadata.comicInfo": {
$exists: true,
$gt: { $size: 0 },
},
},
},
],
publisherWithMostComicsInLibrary: [
{
$unwind:
"$sourcedMetadata.comicvine.volumeInformation.publisher",
},
{
$group: {
_id: "$sourcedMetadata.comicvine.volumeInformation.publisher.name",
count: { $sum: 1 },
},
},
{ $sort: { count: -1 } },
{ $limit: 1 },
],
},
},
]);
return {
totalDocuments: totalCount,
comicDirectorySize,
statistics,
};
} catch (error) {
console.error("Error fetching library statistics:", error);
throw new Error("Failed to fetch library statistics");
}
},
/**
* Search issues using Elasticsearch
*/
searchIssue: async (
_: any,
{
query,
pagination = { size: 10, from: 0 },
type,
}: {
query?: { volumeName?: string; issueNumber?: string };
pagination?: { size?: number; from?: number };
type: string;
},
context: any
) => {
try {
// Get broker from context (set up in GraphQL service)
const broker = context?.broker;
if (!broker) {
throw new Error("Broker not available in context");
}
// Call the search service through the broker
const result = await broker.call("search.issue", {
query: query || {},
pagination,
type,
});
return result;
} catch (error) {
console.error("Error searching issues:", error);
throw new Error(`Failed to search issues: ${error.message}`);
}
},
/**
* List comics with pagination and filtering
*/

View File

@@ -239,6 +239,25 @@ export const typeDefs = gql`
series: String
): ComicConnection!
# Get comic books with advanced pagination and filtering
getComicBooks(
paginationOptions: PaginationOptionsInput!
predicate: PredicateInput
): ComicBooksResult!
# Get comic book groups (volumes with multiple issues)
getComicBookGroups: [ComicBookGroup!]!
# Get library statistics
getLibraryStatistics: LibraryStatistics!
# Search issues using Elasticsearch
searchIssue(
query: SearchIssueQueryInput
pagination: SearchPaginationInput
type: SearchType!
): SearchIssueResult!
# Get user preferences
userPreferences(userId: String = "default"): UserPreferences
@@ -439,4 +458,162 @@ export const typeDefs = gql`
message: String
canonicalMetadataResolved: Boolean!
}
# Pagination options input
input PaginationOptionsInput {
page: Int
limit: Int
sort: String
lean: Boolean
leanWithId: Boolean
offset: Int
pagination: Boolean
}
# Predicate input for filtering
# Note: This is a placeholder. In practice, predicates are passed as JSON objects
# and handled dynamically in the resolver
scalar PredicateInput
# Comic books result with pagination
type ComicBooksResult {
docs: [Comic!]!
totalDocs: Int!
limit: Int!
page: Int
totalPages: Int!
hasNextPage: Boolean!
hasPrevPage: Boolean!
nextPage: Int
prevPage: Int
pagingCounter: Int!
}
# Comic book group (volume with issues)
type ComicBookGroup {
id: ID!
volumes: VolumeInfo
}
# Volume information
type VolumeInfo {
id: Int
name: String
count_of_issues: Int
publisher: Publisher
start_year: String
image: VolumeImage
description: String
site_detail_url: String
}
# Publisher information
type Publisher {
id: Int
name: String
api_detail_url: String
}
# Volume image
type VolumeImage {
icon_url: String
medium_url: String
screen_url: String
screen_large_url: String
small_url: String
super_url: String
thumb_url: String
tiny_url: String
original_url: String
image_tags: String
}
# Library statistics
type LibraryStatistics {
totalDocuments: Int!
comicDirectorySize: DirectorySize!
statistics: [StatisticsFacet!]!
}
# Directory size information
type DirectorySize {
totalSize: Float!
totalSizeInMB: Float!
totalSizeInGB: Float!
fileCount: Int!
}
# Statistics facet
type StatisticsFacet {
fileTypes: [FileTypeStats!]
issues: [IssueStats!]
fileLessComics: [Comic!]
issuesWithComicInfoXML: [Comic!]
publisherWithMostComicsInLibrary: [PublisherStats!]
}
# File type statistics
type FileTypeStats {
id: String!
data: [ID!]!
}
# Issue statistics
type IssueStats {
id: VolumeInfo
data: [ID!]!
}
# Publisher statistics
type PublisherStats {
id: String!
count: Int!
}
# Search issue query input
input SearchIssueQueryInput {
volumeName: String
issueNumber: String
}
# Search pagination input
input SearchPaginationInput {
size: Int
from: Int
}
# Search type enum
enum SearchType {
all
volumeName
wanted
volumes
}
# Search issue result
type SearchIssueResult {
hits: SearchHits!
took: Int
timed_out: Boolean
}
# Search hits
type SearchHits {
total: SearchTotal!
max_score: Float
hits: [SearchHit!]!
}
# Search total
type SearchTotal {
value: Int!
relation: String!
}
# Search hit
type SearchHit {
_index: String!
_id: String!
_score: Float
_source: Comic!
}
`;