421 lines
13 KiB
TypeScript
421 lines
13 KiB
TypeScript
/**
|
|
* Example: Importing Comics with GraphQL and Canonical Metadata
|
|
*
|
|
* This example demonstrates how to import comics using the new GraphQL-based
|
|
* import system that automatically resolves canonical metadata from multiple sources.
|
|
*/
|
|
|
|
import { ServiceBroker } from "moleculer";
|
|
import {
|
|
importComicViaGraphQL,
|
|
updateSourcedMetadataViaGraphQL,
|
|
resolveMetadataViaGraphQL,
|
|
analyzeMetadataConflictsViaGraphQL,
|
|
getComicViaGraphQL,
|
|
} from "../utils/import.graphql.utils";
|
|
|
|
/**
|
|
* Example 1: Basic Comic Import
|
|
* Import a comic with ComicInfo.xml metadata
|
|
*/
|
|
async function example1_basicImport(broker: ServiceBroker) {
|
|
console.log("\n=== Example 1: Basic Comic Import ===\n");
|
|
|
|
const result = await importComicViaGraphQL(broker, {
|
|
filePath: "/comics/amazing-spider-man-001.cbz",
|
|
fileSize: 12345678,
|
|
|
|
rawFileDetails: {
|
|
name: "Amazing Spider-Man 001",
|
|
filePath: "/comics/amazing-spider-man-001.cbz",
|
|
fileSize: 12345678,
|
|
extension: ".cbz",
|
|
mimeType: "application/x-cbz",
|
|
pageCount: 24,
|
|
},
|
|
|
|
inferredMetadata: {
|
|
issue: {
|
|
name: "Amazing Spider-Man",
|
|
number: 1,
|
|
year: "2023",
|
|
},
|
|
},
|
|
|
|
sourcedMetadata: {
|
|
comicInfo: {
|
|
Title: "Amazing Spider-Man #1",
|
|
Series: "Amazing Spider-Man",
|
|
Number: "1",
|
|
Publisher: "Marvel Comics",
|
|
Summary: "Peter Parker's origin story begins...",
|
|
Year: "2023",
|
|
Month: "1",
|
|
},
|
|
},
|
|
});
|
|
|
|
console.log("Import Result:", {
|
|
success: result.success,
|
|
message: result.message,
|
|
canonicalMetadataResolved: result.canonicalMetadataResolved,
|
|
comicId: result.comic.id,
|
|
});
|
|
|
|
console.log("\nCanonical Metadata:");
|
|
console.log(" Title:", result.comic.canonicalMetadata?.title?.value);
|
|
console.log(" Source:", result.comic.canonicalMetadata?.title?.provenance?.source);
|
|
console.log(" Series:", result.comic.canonicalMetadata?.series?.value);
|
|
console.log(" Publisher:", result.comic.canonicalMetadata?.publisher?.value);
|
|
|
|
return result.comic.id;
|
|
}
|
|
|
|
/**
|
|
* Example 2: Import with Multiple Sources
|
|
* Import a comic with metadata from ComicInfo.xml, ComicVine, and LOCG
|
|
*/
|
|
async function example2_multiSourceImport(broker: ServiceBroker) {
|
|
console.log("\n=== Example 2: Multi-Source Import ===\n");
|
|
|
|
const result = await importComicViaGraphQL(broker, {
|
|
filePath: "/comics/batman-001.cbz",
|
|
|
|
rawFileDetails: {
|
|
name: "Batman 001",
|
|
filePath: "/comics/batman-001.cbz",
|
|
fileSize: 15000000,
|
|
extension: ".cbz",
|
|
pageCount: 32,
|
|
},
|
|
|
|
inferredMetadata: {
|
|
issue: {
|
|
name: "Batman",
|
|
number: 1,
|
|
year: "2023",
|
|
},
|
|
},
|
|
|
|
sourcedMetadata: {
|
|
// From ComicInfo.xml
|
|
comicInfo: {
|
|
Title: "Batman #1",
|
|
Series: "Batman",
|
|
Number: "1",
|
|
Publisher: "DC Comics",
|
|
Summary: "The Dark Knight returns...",
|
|
},
|
|
|
|
// From ComicVine API
|
|
comicvine: {
|
|
name: "Batman #1: The Court of Owls",
|
|
issue_number: "1",
|
|
description: "A new era begins for the Dark Knight...",
|
|
cover_date: "2023-01-01",
|
|
volumeInformation: {
|
|
name: "Batman",
|
|
publisher: {
|
|
name: "DC Comics",
|
|
},
|
|
},
|
|
},
|
|
|
|
// From League of Comic Geeks
|
|
locg: {
|
|
name: "Batman #1",
|
|
publisher: "DC Comics",
|
|
description: "Batman faces a new threat...",
|
|
rating: 4.8,
|
|
pulls: 15000,
|
|
cover: "https://example.com/batman-001-cover.jpg",
|
|
},
|
|
},
|
|
});
|
|
|
|
console.log("Import Result:", {
|
|
success: result.success,
|
|
canonicalMetadataResolved: result.canonicalMetadataResolved,
|
|
comicId: result.comic.id,
|
|
});
|
|
|
|
console.log("\nCanonical Metadata (resolved from 3 sources):");
|
|
console.log(" Title:", result.comic.canonicalMetadata?.title?.value);
|
|
console.log(" Source:", result.comic.canonicalMetadata?.title?.provenance?.source);
|
|
console.log(" Confidence:", result.comic.canonicalMetadata?.title?.provenance?.confidence);
|
|
console.log("\n Description:", result.comic.canonicalMetadata?.description?.value?.substring(0, 50) + "...");
|
|
console.log(" Source:", result.comic.canonicalMetadata?.description?.provenance?.source);
|
|
|
|
return result.comic.id;
|
|
}
|
|
|
|
/**
|
|
* Example 3: Update Metadata After Import
|
|
* Import a comic, then fetch and add ComicVine metadata
|
|
*/
|
|
async function example3_updateMetadataAfterImport(broker: ServiceBroker) {
|
|
console.log("\n=== Example 3: Update Metadata After Import ===\n");
|
|
|
|
// Step 1: Import with basic metadata
|
|
console.log("Step 1: Initial import with ComicInfo.xml only");
|
|
const importResult = await importComicViaGraphQL(broker, {
|
|
filePath: "/comics/x-men-001.cbz",
|
|
|
|
rawFileDetails: {
|
|
name: "X-Men 001",
|
|
filePath: "/comics/x-men-001.cbz",
|
|
fileSize: 10000000,
|
|
extension: ".cbz",
|
|
},
|
|
|
|
sourcedMetadata: {
|
|
comicInfo: {
|
|
Title: "X-Men #1",
|
|
Series: "X-Men",
|
|
Number: "1",
|
|
Publisher: "Marvel Comics",
|
|
},
|
|
},
|
|
});
|
|
|
|
const comicId = importResult.comic.id;
|
|
console.log(" Comic imported:", comicId);
|
|
console.log(" Initial title:", importResult.comic.canonicalMetadata?.title?.value);
|
|
console.log(" Initial source:", importResult.comic.canonicalMetadata?.title?.provenance?.source);
|
|
|
|
// Step 2: Fetch and add ComicVine metadata
|
|
console.log("\nStep 2: Adding ComicVine metadata");
|
|
const comicVineData = {
|
|
name: "X-Men #1: Mutant Genesis",
|
|
issue_number: "1",
|
|
description: "The X-Men are reborn in this landmark issue...",
|
|
cover_date: "2023-01-01",
|
|
volumeInformation: {
|
|
name: "X-Men",
|
|
publisher: {
|
|
name: "Marvel Comics",
|
|
},
|
|
},
|
|
};
|
|
|
|
const updatedComic = await updateSourcedMetadataViaGraphQL(
|
|
broker,
|
|
comicId,
|
|
"comicvine",
|
|
comicVineData
|
|
);
|
|
|
|
console.log(" Updated title:", updatedComic.canonicalMetadata?.title?.value);
|
|
console.log(" Updated source:", updatedComic.canonicalMetadata?.title?.provenance?.source);
|
|
console.log(" Description added:", updatedComic.canonicalMetadata?.description?.value?.substring(0, 50) + "...");
|
|
|
|
return comicId;
|
|
}
|
|
|
|
/**
|
|
* Example 4: Analyze Metadata Conflicts
|
|
* See how conflicts between sources are resolved
|
|
*/
|
|
async function example4_analyzeConflicts(broker: ServiceBroker) {
|
|
console.log("\n=== Example 4: Analyze Metadata Conflicts ===\n");
|
|
|
|
// Import with conflicting metadata
|
|
const result = await importComicViaGraphQL(broker, {
|
|
filePath: "/comics/superman-001.cbz",
|
|
|
|
rawFileDetails: {
|
|
name: "Superman 001",
|
|
filePath: "/comics/superman-001.cbz",
|
|
fileSize: 14000000,
|
|
extension: ".cbz",
|
|
},
|
|
|
|
sourcedMetadata: {
|
|
comicInfo: {
|
|
Title: "Superman #1",
|
|
Series: "Superman",
|
|
Publisher: "DC Comics",
|
|
},
|
|
comicvine: {
|
|
name: "Superman #1: Man of Steel",
|
|
volumeInformation: {
|
|
name: "Superman",
|
|
publisher: {
|
|
name: "DC Comics",
|
|
},
|
|
},
|
|
},
|
|
locg: {
|
|
name: "Superman #1 (2023)",
|
|
publisher: "DC",
|
|
},
|
|
},
|
|
});
|
|
|
|
const comicId = result.comic.id;
|
|
console.log("Comic imported:", comicId);
|
|
|
|
// Analyze conflicts
|
|
console.log("\nAnalyzing metadata conflicts...");
|
|
const conflicts = await analyzeMetadataConflictsViaGraphQL(broker, comicId);
|
|
|
|
console.log(`\nFound ${conflicts.length} field(s) with conflicts:\n`);
|
|
|
|
for (const conflict of conflicts) {
|
|
console.log(`Field: ${conflict.field}`);
|
|
console.log(` Candidates:`);
|
|
for (const candidate of conflict.candidates) {
|
|
console.log(` - "${candidate.value}" from ${candidate.provenance.source} (confidence: ${candidate.provenance.confidence})`);
|
|
}
|
|
console.log(` Resolved: "${conflict.resolved.value}" from ${conflict.resolved.provenance.source}`);
|
|
console.log(` Reason: ${conflict.resolutionReason}`);
|
|
console.log();
|
|
}
|
|
|
|
return comicId;
|
|
}
|
|
|
|
/**
|
|
* Example 5: Manual Metadata Resolution
|
|
* Manually trigger metadata resolution
|
|
*/
|
|
async function example5_manualResolution(broker: ServiceBroker) {
|
|
console.log("\n=== Example 5: Manual Metadata Resolution ===\n");
|
|
|
|
// Import without auto-resolution (if disabled)
|
|
const result = await importComicViaGraphQL(broker, {
|
|
filePath: "/comics/wonder-woman-001.cbz",
|
|
|
|
rawFileDetails: {
|
|
name: "Wonder Woman 001",
|
|
filePath: "/comics/wonder-woman-001.cbz",
|
|
fileSize: 13000000,
|
|
extension: ".cbz",
|
|
},
|
|
|
|
sourcedMetadata: {
|
|
comicInfo: {
|
|
Title: "Wonder Woman #1",
|
|
Series: "Wonder Woman",
|
|
},
|
|
},
|
|
});
|
|
|
|
const comicId = result.comic.id;
|
|
console.log("Comic imported:", comicId);
|
|
console.log("Auto-resolved:", result.canonicalMetadataResolved);
|
|
|
|
// Manually trigger resolution
|
|
console.log("\nManually resolving metadata...");
|
|
const resolvedComic = await resolveMetadataViaGraphQL(broker, comicId);
|
|
|
|
console.log("Resolved metadata:");
|
|
console.log(" Title:", resolvedComic.canonicalMetadata?.title?.value);
|
|
console.log(" Series:", resolvedComic.canonicalMetadata?.series?.value);
|
|
|
|
return comicId;
|
|
}
|
|
|
|
/**
|
|
* Example 6: Get Comic with Full Canonical Metadata
|
|
* Retrieve a comic with all its canonical metadata
|
|
*/
|
|
async function example6_getComicWithMetadata(broker: ServiceBroker, comicId: string) {
|
|
console.log("\n=== Example 6: Get Comic with Full Metadata ===\n");
|
|
|
|
const comic = await getComicViaGraphQL(broker, comicId);
|
|
|
|
console.log("Comic ID:", comic.id);
|
|
console.log("\nCanonical Metadata:");
|
|
console.log(" Title:", comic.canonicalMetadata?.title?.value);
|
|
console.log(" Source:", comic.canonicalMetadata?.title?.provenance?.source);
|
|
console.log(" Confidence:", comic.canonicalMetadata?.title?.provenance?.confidence);
|
|
console.log(" Fetched:", comic.canonicalMetadata?.title?.provenance?.fetchedAt);
|
|
console.log(" User Override:", comic.canonicalMetadata?.title?.userOverride || false);
|
|
|
|
console.log("\n Series:", comic.canonicalMetadata?.series?.value);
|
|
console.log(" Source:", comic.canonicalMetadata?.series?.provenance?.source);
|
|
|
|
console.log("\n Publisher:", comic.canonicalMetadata?.publisher?.value);
|
|
console.log(" Source:", comic.canonicalMetadata?.publisher?.provenance?.source);
|
|
|
|
if (comic.canonicalMetadata?.description) {
|
|
console.log("\n Description:", comic.canonicalMetadata.description.value?.substring(0, 100) + "...");
|
|
console.log(" Source:", comic.canonicalMetadata.description.provenance?.source);
|
|
}
|
|
|
|
if (comic.canonicalMetadata?.creators?.length > 0) {
|
|
console.log("\n Creators:");
|
|
for (const creator of comic.canonicalMetadata.creators) {
|
|
console.log(` - ${creator.name} (${creator.role}) from ${creator.provenance.source}`);
|
|
}
|
|
}
|
|
|
|
console.log("\nRaw File Details:");
|
|
console.log(" Name:", comic.rawFileDetails?.name);
|
|
console.log(" Path:", comic.rawFileDetails?.filePath);
|
|
console.log(" Size:", comic.rawFileDetails?.fileSize);
|
|
console.log(" Pages:", comic.rawFileDetails?.pageCount);
|
|
|
|
console.log("\nImport Status:");
|
|
console.log(" Imported:", comic.importStatus?.isImported);
|
|
console.log(" Tagged:", comic.importStatus?.tagged);
|
|
}
|
|
|
|
/**
|
|
* Run all examples
|
|
*/
|
|
async function runAllExamples(broker: ServiceBroker) {
|
|
console.log("╔════════════════════════════════════════════════════════════╗");
|
|
console.log("║ Comic Import with GraphQL & Canonical Metadata Examples ║");
|
|
console.log("╚════════════════════════════════════════════════════════════╝");
|
|
|
|
try {
|
|
// Example 1: Basic import
|
|
const comicId1 = await example1_basicImport(broker);
|
|
|
|
// Example 2: Multi-source import
|
|
const comicId2 = await example2_multiSourceImport(broker);
|
|
|
|
// Example 3: Update after import
|
|
const comicId3 = await example3_updateMetadataAfterImport(broker);
|
|
|
|
// Example 4: Analyze conflicts
|
|
const comicId4 = await example4_analyzeConflicts(broker);
|
|
|
|
// Example 5: Manual resolution
|
|
const comicId5 = await example5_manualResolution(broker);
|
|
|
|
// Example 6: Get full metadata
|
|
await example6_getComicWithMetadata(broker, comicId2);
|
|
|
|
console.log("\n╔════════════════════════════════════════════════════════════╗");
|
|
console.log("║ All examples completed successfully! ║");
|
|
console.log("╚════════════════════════════════════════════════════════════╝\n");
|
|
} catch (error) {
|
|
console.error("\n❌ Error running examples:", error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Usage in your service
|
|
*/
|
|
export {
|
|
example1_basicImport,
|
|
example2_multiSourceImport,
|
|
example3_updateMetadataAfterImport,
|
|
example4_analyzeConflicts,
|
|
example5_manualResolution,
|
|
example6_getComicWithMetadata,
|
|
runAllExamples,
|
|
};
|
|
|
|
// If running directly
|
|
if (require.main === module) {
|
|
console.log("Note: This is an example file. To run these examples:");
|
|
console.log("1. Ensure your Moleculer broker is running");
|
|
console.log("2. Import and call the example functions from your service");
|
|
console.log("3. Or integrate the patterns into your library.service.ts");
|
|
}
|