🪢 Added resolvers for LoCG
This commit is contained in:
196
models/graphql/resolvers.ts
Normal file
196
models/graphql/resolvers.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* GraphQL Resolvers for ThreeTwo Metadata Service
|
||||
* Maps GraphQL queries to Moleculer service actions
|
||||
*/
|
||||
|
||||
export const resolvers = {
|
||||
Query: {
|
||||
/**
|
||||
* Search ComicVine for volumes, issues, characters, etc.
|
||||
*/
|
||||
searchComicVine: async (_: any, { input }: any, context: any) => {
|
||||
const { broker } = context;
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
|
||||
return broker.call("comicvine.search", {
|
||||
query: input.query,
|
||||
resources: input.resources,
|
||||
format: input.format || "json",
|
||||
sort: input.sort,
|
||||
field_list: input.field_list,
|
||||
limit: input.limit?.toString(),
|
||||
offset: input.offset?.toString(),
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Advanced volume-based search with scoring and filtering
|
||||
*/
|
||||
volumeBasedSearch: async (_: any, { input }: any, context: any) => {
|
||||
const { broker } = context;
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
|
||||
const result = await broker.call("comicvine.volumeBasedSearch", {
|
||||
query: input.query,
|
||||
resources: input.resources,
|
||||
format: input.format || "json",
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
fieldList: input.fieldList,
|
||||
scorerConfiguration: input.scorerConfiguration,
|
||||
rawFileDetails: input.rawFileDetails,
|
||||
});
|
||||
|
||||
// Transform the result to match GraphQL schema
|
||||
return {
|
||||
results: result.results || result,
|
||||
totalResults: result.totalResults || result.length || 0,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Get volume details by URI
|
||||
*/
|
||||
getVolume: async (_: any, { input }: any, context: any) => {
|
||||
const { broker } = context;
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
|
||||
return broker.call("comicvine.getVolumes", {
|
||||
volumeURI: input.volumeURI,
|
||||
fieldList: input.fieldList,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all issues for a series by comic object ID
|
||||
*/
|
||||
getIssuesForSeries: async (_: any, { comicObjectId }: any, context: any) => {
|
||||
const { broker } = context;
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
|
||||
return broker.call("comicvine.getIssuesForSeries", {
|
||||
comicObjectId,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get generic ComicVine resource (issues, volumes, etc.)
|
||||
*/
|
||||
getComicVineResource: async (_: any, { input }: any, context: any) => {
|
||||
const { broker } = context;
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
|
||||
return broker.call("comicvine.getResource", {
|
||||
resources: input.resources,
|
||||
filter: input.filter,
|
||||
fieldList: input.fieldList,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get story arcs for a volume
|
||||
*/
|
||||
getStoryArcs: async (_: any, { volumeId }: any, context: any) => {
|
||||
const { broker } = context;
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
|
||||
return broker.call("comicvine.getStoryArcs", {
|
||||
volumeId,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get weekly pull list from League of Comic Geeks
|
||||
*/
|
||||
getWeeklyPullList: async (_: any, { input }: any, context: any) => {
|
||||
const { broker } = context;
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
|
||||
const locgResponse = await broker.call("comicvine.getWeeklyPullList", {
|
||||
startDate: input.startDate,
|
||||
currentPage: input.currentPage.toString(),
|
||||
pageSize: input.pageSize.toString(),
|
||||
});
|
||||
|
||||
// Transform LOCG response to match GraphQL schema
|
||||
return {
|
||||
result: locgResponse.result.map((item: any) => ({
|
||||
name: item.issueName,
|
||||
publisher: item.publisher,
|
||||
url: item.issueUrl,
|
||||
cover: item.coverImageUrl,
|
||||
description: item.description || null,
|
||||
price: item.price || null,
|
||||
rating: item.rating || null,
|
||||
pulls: item.pulls || null,
|
||||
potw: item.potw || null,
|
||||
publicationDate: item.publicationDate || null,
|
||||
})),
|
||||
meta: locgResponse.meta,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch resource from Metron API
|
||||
*/
|
||||
fetchMetronResource: async (_: any, { input }: any, context: any) => {
|
||||
const { broker } = context;
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
|
||||
const result = await broker.call("metron.fetchResource", {
|
||||
resource: input.resource,
|
||||
method: input.method,
|
||||
query: input.query,
|
||||
});
|
||||
|
||||
return {
|
||||
data: result,
|
||||
status: 200,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
Mutation: {
|
||||
/**
|
||||
* Placeholder for future mutations
|
||||
*/
|
||||
_empty: (): null => null,
|
||||
},
|
||||
|
||||
// Custom scalar resolver for JSON
|
||||
JSON: {
|
||||
__parseValue(value: any): any {
|
||||
return value;
|
||||
},
|
||||
__serialize(value: any): any {
|
||||
return value;
|
||||
},
|
||||
__parseLiteral(ast: any): any {
|
||||
return ast.value;
|
||||
},
|
||||
},
|
||||
};
|
||||
357
models/graphql/typedef.ts
Normal file
357
models/graphql/typedef.ts
Normal file
@@ -0,0 +1,357 @@
|
||||
import { gql } from "graphql-tag";
|
||||
|
||||
/**
|
||||
* GraphQL Type Definitions for ThreeTwo Metadata Service
|
||||
* Covers ComicVine and Metron API endpoints
|
||||
*/
|
||||
export const typeDefs = gql`
|
||||
# ============================================
|
||||
# ComicVine Types
|
||||
# ============================================
|
||||
|
||||
# Image URLs for various sizes
|
||||
type ImageUrls {
|
||||
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
|
||||
}
|
||||
|
||||
# Publisher information
|
||||
type Publisher {
|
||||
id: Int
|
||||
name: String
|
||||
api_detail_url: String
|
||||
}
|
||||
|
||||
# Volume information
|
||||
type Volume {
|
||||
id: Int!
|
||||
name: String!
|
||||
api_detail_url: String
|
||||
site_detail_url: String
|
||||
start_year: String
|
||||
publisher: Publisher
|
||||
count_of_issues: Int
|
||||
image: ImageUrls
|
||||
description: String
|
||||
deck: String
|
||||
}
|
||||
|
||||
# Issue information
|
||||
type Issue {
|
||||
id: Int!
|
||||
name: String
|
||||
issue_number: String
|
||||
api_detail_url: String
|
||||
site_detail_url: String
|
||||
cover_date: String
|
||||
store_date: String
|
||||
volume: Volume
|
||||
image: ImageUrls
|
||||
description: String
|
||||
person_credits: [PersonCredit!]
|
||||
character_credits: [CharacterCredit!]
|
||||
team_credits: [TeamCredit!]
|
||||
location_credits: [LocationCredit!]
|
||||
story_arc_credits: [StoryArcCredit!]
|
||||
}
|
||||
|
||||
# Person credit (writer, artist, etc.)
|
||||
type PersonCredit {
|
||||
id: Int
|
||||
name: String
|
||||
api_detail_url: String
|
||||
site_detail_url: String
|
||||
role: String
|
||||
}
|
||||
|
||||
# Character credit
|
||||
type CharacterCredit {
|
||||
id: Int
|
||||
name: String
|
||||
api_detail_url: String
|
||||
site_detail_url: String
|
||||
}
|
||||
|
||||
# Team credit
|
||||
type TeamCredit {
|
||||
id: Int
|
||||
name: String
|
||||
api_detail_url: String
|
||||
site_detail_url: String
|
||||
}
|
||||
|
||||
# Location credit
|
||||
type LocationCredit {
|
||||
id: Int
|
||||
name: String
|
||||
api_detail_url: String
|
||||
site_detail_url: String
|
||||
}
|
||||
|
||||
# Story arc credit
|
||||
type StoryArcCredit {
|
||||
id: Int
|
||||
name: String
|
||||
api_detail_url: String
|
||||
site_detail_url: String
|
||||
deck: String
|
||||
description: String
|
||||
image: ImageUrls
|
||||
}
|
||||
|
||||
# ComicVine search result
|
||||
type ComicVineSearchResult {
|
||||
error: String!
|
||||
limit: Int!
|
||||
offset: Int!
|
||||
number_of_page_results: Int!
|
||||
number_of_total_results: Int!
|
||||
status_code: Int!
|
||||
results: [SearchResultItem!]!
|
||||
}
|
||||
|
||||
# Generic search result item (can be volume, issue, etc.)
|
||||
type SearchResultItem {
|
||||
id: Int
|
||||
name: String
|
||||
api_detail_url: String
|
||||
site_detail_url: String
|
||||
image: ImageUrls
|
||||
description: String
|
||||
deck: String
|
||||
# Volume-specific fields
|
||||
start_year: String
|
||||
publisher: Publisher
|
||||
count_of_issues: Int
|
||||
# Issue-specific fields
|
||||
issue_number: String
|
||||
volume: Volume
|
||||
cover_date: String
|
||||
}
|
||||
|
||||
# Volume-based search result with scoring
|
||||
type VolumeSearchResult {
|
||||
volume: Volume!
|
||||
score: Float
|
||||
matchedIssues: [Issue!]
|
||||
}
|
||||
|
||||
# Volume-based search response
|
||||
type VolumeBasedSearchResponse {
|
||||
results: [VolumeSearchResult!]!
|
||||
totalResults: Int!
|
||||
}
|
||||
|
||||
# Weekly pull list item (from League of Comic Geeks)
|
||||
type PullListItem {
|
||||
name: String
|
||||
publisher: String
|
||||
url: String
|
||||
cover: String
|
||||
description: String
|
||||
price: String
|
||||
rating: Float
|
||||
pulls: Int
|
||||
potw: Int
|
||||
publicationDate: String
|
||||
}
|
||||
|
||||
# Paginated pull list response
|
||||
type PullListResponse {
|
||||
result: [PullListItem!]!
|
||||
meta: PaginationMeta!
|
||||
}
|
||||
|
||||
# Pagination metadata
|
||||
type PaginationMeta {
|
||||
currentPage: Int!
|
||||
totalPages: Int!
|
||||
pageSize: Int!
|
||||
totalCount: Int!
|
||||
hasNextPage: Boolean!
|
||||
hasPreviousPage: Boolean!
|
||||
}
|
||||
|
||||
# Story arc with enriched data
|
||||
type StoryArc {
|
||||
id: Int!
|
||||
name: String!
|
||||
deck: String
|
||||
description: String
|
||||
image: ImageUrls
|
||||
issues: [Issue!]
|
||||
}
|
||||
|
||||
# Generic ComicVine resource response
|
||||
type ComicVineResourceResponse {
|
||||
error: String!
|
||||
limit: Int!
|
||||
offset: Int!
|
||||
number_of_page_results: Int!
|
||||
number_of_total_results: Int!
|
||||
status_code: Int!
|
||||
results: [SearchResultItem!]!
|
||||
}
|
||||
|
||||
# Volume detail response
|
||||
type VolumeDetailResponse {
|
||||
error: String!
|
||||
status_code: Int!
|
||||
results: Volume!
|
||||
}
|
||||
|
||||
# Issues for series response
|
||||
type IssuesForSeriesResponse {
|
||||
error: String!
|
||||
limit: Int!
|
||||
offset: Int!
|
||||
number_of_page_results: Int!
|
||||
number_of_total_results: Int!
|
||||
status_code: Int!
|
||||
results: [Issue!]!
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Metron Types
|
||||
# ============================================
|
||||
|
||||
# Generic Metron resource (flexible JSON response)
|
||||
scalar JSON
|
||||
|
||||
type MetronResponse {
|
||||
data: JSON
|
||||
status: Int!
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Input Types
|
||||
# ============================================
|
||||
|
||||
# Search parameters
|
||||
input SearchInput {
|
||||
query: String!
|
||||
resources: String!
|
||||
format: String
|
||||
sort: String
|
||||
field_list: String
|
||||
limit: Int
|
||||
offset: Int
|
||||
}
|
||||
|
||||
# Volume-based search configuration
|
||||
input VolumeSearchInput {
|
||||
query: String!
|
||||
resources: String!
|
||||
format: String
|
||||
limit: Int
|
||||
offset: Int
|
||||
fieldList: String
|
||||
scorerConfiguration: ScorerConfigurationInput
|
||||
rawFileDetails: JSON
|
||||
}
|
||||
|
||||
# Scorer configuration for matching
|
||||
input ScorerConfigurationInput {
|
||||
searchParams: SearchParamsInput
|
||||
}
|
||||
|
||||
# Search parameters for scoring
|
||||
input SearchParamsInput {
|
||||
name: String
|
||||
number: String
|
||||
year: String
|
||||
volume: String
|
||||
}
|
||||
|
||||
# Get volumes input
|
||||
input GetVolumesInput {
|
||||
volumeURI: String!
|
||||
fieldList: String
|
||||
}
|
||||
|
||||
# Get resource input
|
||||
input GetResourceInput {
|
||||
resources: String!
|
||||
filter: String
|
||||
fieldList: String
|
||||
}
|
||||
|
||||
# Weekly pull list input
|
||||
input WeeklyPullListInput {
|
||||
startDate: String!
|
||||
currentPage: Int!
|
||||
pageSize: Int!
|
||||
}
|
||||
|
||||
# Metron fetch resource input
|
||||
input MetronFetchInput {
|
||||
resource: String!
|
||||
method: String!
|
||||
query: String
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Queries
|
||||
# ============================================
|
||||
|
||||
type Query {
|
||||
"""
|
||||
Search ComicVine for volumes, issues, characters, etc.
|
||||
"""
|
||||
searchComicVine(input: SearchInput!): ComicVineSearchResult!
|
||||
|
||||
"""
|
||||
Advanced volume-based search with scoring and filtering
|
||||
"""
|
||||
volumeBasedSearch(input: VolumeSearchInput!): VolumeBasedSearchResponse!
|
||||
|
||||
"""
|
||||
Get volume details by URI
|
||||
"""
|
||||
getVolume(input: GetVolumesInput!): VolumeDetailResponse!
|
||||
|
||||
"""
|
||||
Get all issues for a series by comic object ID
|
||||
"""
|
||||
getIssuesForSeries(comicObjectId: ID!): IssuesForSeriesResponse!
|
||||
|
||||
"""
|
||||
Get generic ComicVine resource (issues, volumes, etc.)
|
||||
"""
|
||||
getComicVineResource(input: GetResourceInput!): ComicVineResourceResponse!
|
||||
|
||||
"""
|
||||
Get story arcs for a volume
|
||||
"""
|
||||
getStoryArcs(volumeId: Int!): [StoryArc!]!
|
||||
|
||||
"""
|
||||
Get weekly pull list from League of Comic Geeks
|
||||
"""
|
||||
getWeeklyPullList(input: WeeklyPullListInput!): PullListResponse!
|
||||
|
||||
"""
|
||||
Fetch resource from Metron API
|
||||
"""
|
||||
fetchMetronResource(input: MetronFetchInput!): MetronResponse!
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Mutations
|
||||
# ============================================
|
||||
|
||||
type Mutation {
|
||||
"""
|
||||
Placeholder for future mutations
|
||||
"""
|
||||
_empty: String
|
||||
}
|
||||
`;
|
||||
Reference in New Issue
Block a user