🛠 graphql changes
This commit is contained in:
213
services/graphql.service.ts
Normal file
213
services/graphql.service.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
import { Service, ServiceBroker } from "moleculer";
|
||||
import { ApolloServer } from "@apollo/server";
|
||||
import { typeDefs } from "../models/graphql/typedef";
|
||||
import { resolvers } from "../models/graphql/resolvers";
|
||||
|
||||
/**
|
||||
* GraphQL Service
|
||||
* Provides a GraphQL API for canonical metadata queries and mutations
|
||||
* Integrates Apollo Server with Moleculer
|
||||
*/
|
||||
export default class GraphQLService extends Service {
|
||||
private apolloServer?: ApolloServer;
|
||||
|
||||
public constructor(broker: ServiceBroker) {
|
||||
super(broker);
|
||||
|
||||
this.parseServiceSchema({
|
||||
name: "graphql",
|
||||
|
||||
settings: {
|
||||
// GraphQL endpoint path
|
||||
path: "/graphql",
|
||||
},
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* Execute a GraphQL query
|
||||
*/
|
||||
query: {
|
||||
params: {
|
||||
query: "string",
|
||||
variables: { type: "object", optional: true },
|
||||
operationName: { type: "string", optional: true },
|
||||
},
|
||||
async handler(ctx: any) {
|
||||
try {
|
||||
if (!this.apolloServer) {
|
||||
throw new Error("Apollo Server not initialized");
|
||||
}
|
||||
|
||||
const { query, variables, operationName } = ctx.params;
|
||||
|
||||
const response = await this.apolloServer.executeOperation(
|
||||
{
|
||||
query,
|
||||
variables,
|
||||
operationName,
|
||||
},
|
||||
{
|
||||
contextValue: {
|
||||
broker: this.broker,
|
||||
ctx,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.body.kind === "single") {
|
||||
return response.body.singleResult;
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.logger.error("GraphQL query error:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Get GraphQL schema
|
||||
*/
|
||||
getSchema: {
|
||||
async handler() {
|
||||
return {
|
||||
typeDefs: typeDefs.loc?.source.body || "",
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Initialize Apollo Server
|
||||
*/
|
||||
async initApolloServer() {
|
||||
this.logger.info("Initializing Apollo Server...");
|
||||
|
||||
this.apolloServer = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
introspection: true, // Enable GraphQL Playground in development
|
||||
formatError: (error) => {
|
||||
this.logger.error("GraphQL Error:", error);
|
||||
return {
|
||||
message: error.message,
|
||||
locations: error.locations,
|
||||
path: error.path,
|
||||
extensions: {
|
||||
code: error.extensions?.code,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
await this.apolloServer.start();
|
||||
this.logger.info("Apollo Server started successfully");
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop Apollo Server
|
||||
*/
|
||||
async stopApolloServer() {
|
||||
if (this.apolloServer) {
|
||||
this.logger.info("Stopping Apollo Server...");
|
||||
await this.apolloServer.stop();
|
||||
this.apolloServer = undefined;
|
||||
this.logger.info("Apollo Server stopped");
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
events: {
|
||||
/**
|
||||
* Trigger metadata resolution when new metadata is imported
|
||||
*/
|
||||
"metadata.imported": {
|
||||
async handler(ctx: any) {
|
||||
const { comicId, source } = ctx.params;
|
||||
this.logger.info(
|
||||
`Metadata imported for comic ${comicId} from ${source}`
|
||||
);
|
||||
|
||||
// Optionally trigger auto-resolution if enabled
|
||||
try {
|
||||
const UserPreferences = require("../models/userpreferences.model").default;
|
||||
const preferences = await UserPreferences.findOne({
|
||||
userId: "default",
|
||||
});
|
||||
|
||||
if (
|
||||
preferences?.autoMerge?.enabled &&
|
||||
preferences?.autoMerge?.onMetadataUpdate
|
||||
) {
|
||||
this.logger.info(
|
||||
`Auto-resolving metadata for comic ${comicId}`
|
||||
);
|
||||
await this.broker.call("graphql.query", {
|
||||
query: `
|
||||
mutation ResolveMetadata($comicId: ID!) {
|
||||
resolveMetadata(comicId: $comicId) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { comicId },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error("Error in auto-resolution:", error);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger metadata resolution when comic is imported
|
||||
*/
|
||||
"comic.imported": {
|
||||
async handler(ctx: any) {
|
||||
const { comicId } = ctx.params;
|
||||
this.logger.info(`Comic imported: ${comicId}`);
|
||||
|
||||
// Optionally trigger auto-resolution if enabled
|
||||
try {
|
||||
const UserPreferences = require("../models/userpreferences.model").default;
|
||||
const preferences = await UserPreferences.findOne({
|
||||
userId: "default",
|
||||
});
|
||||
|
||||
if (
|
||||
preferences?.autoMerge?.enabled &&
|
||||
preferences?.autoMerge?.onImport
|
||||
) {
|
||||
this.logger.info(
|
||||
`Auto-resolving metadata for newly imported comic ${comicId}`
|
||||
);
|
||||
await this.broker.call("graphql.query", {
|
||||
query: `
|
||||
mutation ResolveMetadata($comicId: ID!) {
|
||||
resolveMetadata(comicId: $comicId) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { comicId },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error("Error in auto-resolution on import:", error);
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
started: async function (this: any) {
|
||||
await this.initApolloServer();
|
||||
},
|
||||
|
||||
stopped: async function (this: any) {
|
||||
await this.stopApolloServer();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user