⬇ Import flow fixes
This commit is contained in:
@@ -622,6 +622,58 @@ export const resolvers = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get import statistics for a directory
|
||||
* @async
|
||||
* @function getImportStatistics
|
||||
* @param {any} _ - Parent resolver (unused)
|
||||
* @param {Object} args - Query arguments
|
||||
* @param {string} [args.directoryPath] - Optional directory path to analyze
|
||||
* @param {Object} context - GraphQL context with broker
|
||||
* @returns {Promise<Object>} Import statistics including total files, imported count, and new files
|
||||
* @throws {Error} If statistics calculation fails
|
||||
* @description Analyzes a directory (or default COMICS_DIRECTORY) and compares
|
||||
* files against the database to determine import status. This performs a full
|
||||
* filesystem scan and is slower than getCachedImportStatistics.
|
||||
*
|
||||
* @example
|
||||
* ```graphql
|
||||
* query {
|
||||
* getImportStatistics(directoryPath: "/path/to/comics") {
|
||||
* success
|
||||
* directory
|
||||
* stats {
|
||||
* totalLocalFiles
|
||||
* alreadyImported
|
||||
* newFiles
|
||||
* percentageImported
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
getImportStatistics: async (
|
||||
_: any,
|
||||
{ directoryPath }: { directoryPath?: string },
|
||||
context: any
|
||||
) => {
|
||||
try {
|
||||
const broker = context?.broker;
|
||||
|
||||
if (!broker) {
|
||||
throw new Error("Broker not available in context");
|
||||
}
|
||||
|
||||
const result = await broker.call("library.getImportStatistics", {
|
||||
directoryPath,
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("Error fetching import statistics:", error);
|
||||
throw new Error(`Failed to fetch import statistics: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get cached import statistics (fast, real-time)
|
||||
* @async
|
||||
|
||||
@@ -92,6 +92,7 @@ export default class ApiService extends Service {
|
||||
aliases: {
|
||||
"POST /": "graphql.graphql",
|
||||
"GET /": "graphql.graphql",
|
||||
"GET /health": "graphql.checkRemoteSchema",
|
||||
},
|
||||
mappingPolicy: "restrict",
|
||||
bodyParsers: {
|
||||
|
||||
@@ -114,6 +114,36 @@ export default {
|
||||
},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* Check remote schema health and availability
|
||||
* @returns Status of remote schema connection with appropriate HTTP status
|
||||
*/
|
||||
checkRemoteSchema: {
|
||||
async handler(ctx: Context<any>) {
|
||||
const status: any = {
|
||||
remoteSchemaAvailable: this.remoteSchemaAvailable || false,
|
||||
remoteUrl: this.settings.metadataGraphqlUrl,
|
||||
localSchemaOnly: !this.remoteSchemaAvailable,
|
||||
};
|
||||
|
||||
if (this.remoteSchemaAvailable && this.schema) {
|
||||
const queryType = this.schema.getQueryType();
|
||||
if (queryType) {
|
||||
const fields = Object.keys(queryType.getFields());
|
||||
status.availableQueryFields = fields;
|
||||
status.hasWeeklyPullList = fields.includes('getWeeklyPullList');
|
||||
}
|
||||
}
|
||||
|
||||
// Set HTTP status code based on schema stitching status
|
||||
// 200 = Schema stitching complete (remote available)
|
||||
// 503 = Service degraded (local only, remote unavailable)
|
||||
(ctx.meta as any).$statusCode = this.remoteSchemaAvailable ? 200 : 503;
|
||||
|
||||
return status;
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute GraphQL queries and mutations
|
||||
* @param query - GraphQL query or mutation string
|
||||
@@ -196,11 +226,19 @@ export default {
|
||||
this.logger.info(`Attempting to introspect remote schema at ${this.settings.metadataGraphqlUrl}`);
|
||||
|
||||
const remoteSchema = await fetchRemoteSchema(this.settings.metadataGraphqlUrl);
|
||||
this.logger.info("Successfully introspected remote metadata schema");
|
||||
this.logger.info("✓ Successfully introspected remote metadata schema");
|
||||
|
||||
const remoteQueryType = remoteSchema.getQueryType();
|
||||
const remoteMutationType = remoteSchema.getMutationType();
|
||||
|
||||
if (remoteQueryType) {
|
||||
this.logger.info(`Remote schema Query fields: ${Object.keys(remoteQueryType.getFields()).join(', ')}`);
|
||||
const remoteQueryFields = Object.keys(remoteQueryType.getFields());
|
||||
this.logger.info(`✓ Remote schema has ${remoteQueryFields.length} Query fields: ${remoteQueryFields.join(', ')}`);
|
||||
}
|
||||
|
||||
if (remoteMutationType) {
|
||||
const remoteMutationFields = Object.keys(remoteMutationType.getFields());
|
||||
this.logger.info(`✓ Remote schema has ${remoteMutationFields.length} Mutation fields: ${remoteMutationFields.join(', ')}`);
|
||||
}
|
||||
|
||||
this.schema = stitchSchemas({
|
||||
@@ -212,15 +250,35 @@ export default {
|
||||
});
|
||||
|
||||
const stitchedQueryType = this.schema.getQueryType();
|
||||
const stitchedMutationType = this.schema.getMutationType();
|
||||
|
||||
if (stitchedQueryType) {
|
||||
this.logger.info(`Stitched schema Query fields: ${Object.keys(stitchedQueryType.getFields()).join(', ')}`);
|
||||
const stitchedQueryFields = Object.keys(stitchedQueryType.getFields());
|
||||
this.logger.info(`✓ Stitched schema has ${stitchedQueryFields.length} Query fields`);
|
||||
|
||||
// Verify critical remote fields are present
|
||||
const criticalFields = ['getWeeklyPullList'];
|
||||
const missingFields = criticalFields.filter(field => !stitchedQueryFields.includes(field));
|
||||
if (missingFields.length > 0) {
|
||||
this.logger.warn(`⚠ Missing expected remote fields: ${missingFields.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.info("Successfully stitched local and remote schemas");
|
||||
if (stitchedMutationType) {
|
||||
const stitchedMutationFields = Object.keys(stitchedMutationType.getFields());
|
||||
this.logger.info(`✓ Stitched schema has ${stitchedMutationFields.length} Mutation fields`);
|
||||
}
|
||||
|
||||
this.logger.info("✓ Successfully stitched local and remote schemas");
|
||||
this.remoteSchemaAvailable = true;
|
||||
} catch (remoteError: any) {
|
||||
this.logger.warn(`Could not connect to remote metadata GraphQL at ${this.settings.metadataGraphqlUrl}: ${remoteError.message}`);
|
||||
this.logger.warn("Continuing with local schema only");
|
||||
this.logger.error(`✗ Failed to connect to remote metadata GraphQL at ${this.settings.metadataGraphqlUrl}`);
|
||||
this.logger.error(`✗ Error: ${remoteError.message}`);
|
||||
this.logger.warn("⚠ FALLING BACK TO LOCAL SCHEMA ONLY");
|
||||
this.logger.warn("⚠ Remote queries like 'getWeeklyPullList' will NOT be available");
|
||||
this.logger.warn(`⚠ To fix: Ensure metadata-graphql service is running at ${this.settings.metadataGraphqlUrl}`);
|
||||
this.schema = localSchema;
|
||||
this.remoteSchemaAvailable = false;
|
||||
}
|
||||
|
||||
this.logger.info("GraphQL service started successfully");
|
||||
|
||||
@@ -623,7 +623,7 @@ export default class ImportService extends Service {
|
||||
console.log(
|
||||
"[GraphQL Import] Triggering metadata resolution..."
|
||||
);
|
||||
await this.broker.call("graphql.query", {
|
||||
await this.broker.call("graphql.graphql", {
|
||||
query: `
|
||||
mutation ResolveMetadata($comicId: ID!) {
|
||||
resolveMetadata(comicId: $comicId) {
|
||||
|
||||
@@ -135,7 +135,7 @@ export async function importComicViaGraphQL(
|
||||
}
|
||||
|
||||
try {
|
||||
const result: any = await broker.call("graphql.query", {
|
||||
const result: any = await broker.call("graphql.graphql", {
|
||||
query: mutation,
|
||||
variables: { input },
|
||||
});
|
||||
@@ -183,7 +183,7 @@ export async function updateSourcedMetadataViaGraphQL(
|
||||
`;
|
||||
|
||||
try {
|
||||
const result: any = await broker.call("graphql.query", {
|
||||
const result: any = await broker.call("graphql.graphql", {
|
||||
query: mutation,
|
||||
variables: {
|
||||
comicId,
|
||||
@@ -229,7 +229,7 @@ export async function resolveMetadataViaGraphQL(
|
||||
`;
|
||||
|
||||
try {
|
||||
const result: any = await broker.call("graphql.query", {
|
||||
const result: any = await broker.call("graphql.graphql", {
|
||||
query: mutation,
|
||||
variables: { comicId },
|
||||
});
|
||||
@@ -287,7 +287,7 @@ export async function getComicViaGraphQL(
|
||||
`;
|
||||
|
||||
try {
|
||||
const result: any = await broker.call("graphql.query", {
|
||||
const result: any = await broker.call("graphql.graphql", {
|
||||
query,
|
||||
variables: { id: comicId },
|
||||
});
|
||||
@@ -336,7 +336,7 @@ export async function analyzeMetadataConflictsViaGraphQL(
|
||||
`;
|
||||
|
||||
try {
|
||||
const result: any = await broker.call("graphql.query", {
|
||||
const result: any = await broker.call("graphql.graphql", {
|
||||
query,
|
||||
variables: { comicId },
|
||||
});
|
||||
@@ -373,7 +373,7 @@ export async function bulkResolveMetadataViaGraphQL(
|
||||
`;
|
||||
|
||||
try {
|
||||
const result: any = await broker.call("graphql.query", {
|
||||
const result: any = await broker.call("graphql.graphql", {
|
||||
query: mutation,
|
||||
variables: { comicIds },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user