@graphql-mesh/neo4j
Version:
122 lines (121 loc) • 4.79 kB
JavaScript
import { GraphQLBigInt } from 'graphql-scalars';
import neo4j from 'neo4j-driver';
import { process } from '@graphql-mesh/cross-helpers';
import { PredefinedProxyOptions } from '@graphql-mesh/store';
import { readFileOrUrl } from '@graphql-mesh/utils';
import { createDefaultExecutor } from '@graphql-tools/delegate';
import { Neo4jGraphQL } from '@neo4j/graphql';
import { toGraphQLTypeDefs } from '@neo4j/introspector';
function getEventEmitterFromPubSub(pubsub) {
return {
on(event, listener) {
pubsub.subscribe(event.toString(), listener);
return this;
},
once(event, listener) {
const id = pubsub.subscribe(event.toString(), data => {
listener(data);
pubsub.unsubscribe(id);
});
return this;
},
emit(event, ...args) {
pubsub.publish(event.toString(), args[0]);
return true;
},
addListener(event, listener) {
return this.on(event, listener);
},
setMaxListeners() {
return this;
},
};
}
export default class Neo4JHandler {
constructor({ config, baseDir, pubsub, store, logger, importFn, }) {
this.config = config;
this.baseDir = baseDir;
this.pubsub = pubsub;
this.typeDefs = store.proxy('typeDefs', PredefinedProxyOptions.StringWithoutValidation);
this.logger = logger;
this.importFn = importFn;
}
getCachedTypeDefs(driver) {
return this.typeDefs.getWithSet(async () => {
if (this.config.source) {
return readFileOrUrl(this.config.source, {
cwd: this.baseDir,
allowUnknownExtensions: true,
importFn: this.importFn,
fetch: this.fetchFn,
logger: this.logger,
});
}
else {
this.logger.info('Inferring the schema from the database: ', `"${this.config.database || 'neo4j'}"`);
let replaceAllPolyfilled = false;
if (!String.prototype.replaceAll) {
replaceAllPolyfilled = true;
// eslint-disable-next-line no-extend-native
String.prototype.replaceAll = function (str, newStr) {
if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') {
return this.replace(str, newStr);
}
return this.replace(new RegExp(str, 'g'), newStr);
};
}
const typeDefs = await toGraphQLTypeDefs(() => driver.session({ database: this.config.database, defaultAccessMode: neo4j.session.READ }));
if (replaceAllPolyfilled) {
// eslint-disable-next-line no-extend-native
delete String.prototype.replaceAll;
}
return typeDefs;
}
});
}
async getMeshSource({ fetchFn }) {
this.fetchFn = fetchFn;
const driver = neo4j.driver(this.config.endpoint, neo4j.auth.basic(this.config.username, this.config.password), {
useBigInt: true,
logging: {
logger: (level, message) => this.logger[level](message),
},
});
const id = this.pubsub.subscribe('destroy', async () => {
this.pubsub.unsubscribe(id);
this.logger.debug('Closing Neo4j');
await driver.close();
this.logger.debug('Neo4j closed');
});
const typeDefs = await this.getCachedTypeDefs(driver);
const events = getEventEmitterFromPubSub(this.pubsub);
const neo4jGraphQL = new Neo4jGraphQL({
typeDefs,
driver,
validate: false,
debug: !!process.env.DEBUG,
resolvers: {
BigInt: GraphQLBigInt,
},
features: {
subscriptions: {
events,
publish: eventMeta => this.pubsub.publish(eventMeta.event, eventMeta),
},
},
});
const schema = await neo4jGraphQL.getSchema();
const defaultExecutor = createDefaultExecutor(schema);
const sessionConfig = {
database: this.config.database,
};
return {
schema: await neo4jGraphQL.getSchema(),
executor(executionRequest) {
executionRequest.context = executionRequest.context || {};
executionRequest.context.sessionConfig = sessionConfig;
return defaultExecutor(executionRequest);
},
};
}
}