database-connector for neo4j
This commit is contained in:
parent
f83eac3ccc
commit
b142a1eded
|
@ -33,3 +33,6 @@ lerna-debug.log*
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
# Environement
|
||||||
|
.env
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
# DO NOT COMMIT THIS FILE!
|
||||||
|
# 1. Validate that the .gitignore file includes .env
|
||||||
|
# 2. Update the below template with the correct values
|
||||||
|
# 3. Rename this file to .env
|
||||||
|
|
||||||
|
DB_HOST=database_host_name
|
||||||
|
DB_PORT=database_port
|
||||||
|
DB_USER=database_user
|
||||||
|
DB_PASS=database_password
|
|
@ -16,7 +16,9 @@
|
||||||
"@nestjs/graphql": "^12.0.1",
|
"@nestjs/graphql": "^12.0.1",
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
"apollo-server-core": "^3.12.0",
|
"apollo-server-core": "^3.12.0",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
|
"neo4j-driver": "^5.11.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"ts-morph": "^19.0.0"
|
"ts-morph": "^19.0.0"
|
||||||
|
@ -3582,7 +3584,6 @@
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
@ -4346,6 +4347,17 @@
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||||
|
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
|
@ -5465,7 +5477,6 @@
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
@ -6844,6 +6855,62 @@
|
||||||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/neo4j-driver": {
|
||||||
|
"version": "5.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/neo4j-driver/-/neo4j-driver-5.11.0.tgz",
|
||||||
|
"integrity": "sha512-2IPKXH9najfKJyczIZ8R15p/oYsb4P+nwp76XRjO46Zl+ssc22+gMe/8FLRYw3tRJYc3b87ikx2s8ZNuseOAxQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"neo4j-driver-bolt-connection": "5.11.0",
|
||||||
|
"neo4j-driver-core": "5.11.0",
|
||||||
|
"rxjs": "^7.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/neo4j-driver-bolt-connection": {
|
||||||
|
"version": "5.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/neo4j-driver-bolt-connection/-/neo4j-driver-bolt-connection-5.11.0.tgz",
|
||||||
|
"integrity": "sha512-jfptm6W/a4CIoip4S/KubxrPIIV3hdOJ8B5t2RtMJwVfup8uJFzRsQLW/ljg7PJdMiE1hHQ94/qcVKd3gCC3og==",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer": "^6.0.3",
|
||||||
|
"neo4j-driver-core": "5.11.0",
|
||||||
|
"string_decoder": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/neo4j-driver-bolt-connection/node_modules/buffer": {
|
||||||
|
"version": "6.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||||
|
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.1",
|
||||||
|
"ieee754": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/neo4j-driver-bolt-connection/node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/neo4j-driver-core": {
|
||||||
|
"version": "5.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/neo4j-driver-core/-/neo4j-driver-core-5.11.0.tgz",
|
||||||
|
"integrity": "sha512-HIZrX1wIkwb1BlXtDk0thbyzYrlDKQK9PuzcgeKF9/fTORxr5K39kdIiwVi3gkoGOcFCSoBu+fTnlnav1BcgRg=="
|
||||||
|
},
|
||||||
"node_modules/node-abort-controller": {
|
"node_modules/node-abort-controller": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
|
||||||
|
|
|
@ -27,7 +27,9 @@
|
||||||
"@nestjs/graphql": "^12.0.1",
|
"@nestjs/graphql": "^12.0.1",
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
"apollo-server-core": "^3.12.0",
|
"apollo-server-core": "^3.12.0",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
|
"neo4j-driver": "^5.11.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"ts-morph": "^19.0.0"
|
"ts-morph": "^19.0.0"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Module, Post } from '@nestjs/common';
|
import 'dotenv/config';
|
||||||
|
import { Injectable, Logger, Module, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { GraphQLModule } from '@nestjs/graphql';
|
import { GraphQLModule } from '@nestjs/graphql';
|
||||||
|
@ -8,6 +9,41 @@ import { MemberResolver } from './services/reputation/components/member/member.r
|
||||||
import { PostResolver } from './services/reputation/components/post/post.resolver';
|
import { PostResolver } from './services/reputation/components/post/post.resolver';
|
||||||
import { CitationsResolver } from './services/reputation/components/citation/citation.resolver';
|
import { CitationsResolver } from './services/reputation/components/citation/citation.resolver';
|
||||||
|
|
||||||
|
import { DatabaseConnector } from './util/database-connector';
|
||||||
|
import { MemberRepository } from './services/reputation/components/member/member.repository';
|
||||||
|
const dbConnector = new DatabaseConnector(new Logger('DatabaseConnector'));
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class StartupService implements OnModuleInit {
|
||||||
|
async onModuleInit() {
|
||||||
|
|
||||||
|
const testSuite = process.env.TEST_SUITE;
|
||||||
|
if (testSuite?.toLowerCase().includes("integration")) return;
|
||||||
|
|
||||||
|
const host = process.env.DB_HOST;
|
||||||
|
const port = process.env.DB_PORT;
|
||||||
|
const user = process.env.DB_USER;
|
||||||
|
const pass = process.env.DB_PASS;
|
||||||
|
|
||||||
|
if (!host) throw new Error('Missing environment variable, DB_HOST');
|
||||||
|
if (!port) throw new Error('Missing environment variable, DB_PORT');
|
||||||
|
if (!user) throw new Error('Missing environment variable, DB_USER');
|
||||||
|
if (!pass) throw new Error('Missing environment variable, DB_PASSWORD');
|
||||||
|
|
||||||
|
const options = {host, port, user, pass};
|
||||||
|
await dbConnector.connect(options, [
|
||||||
|
MemberRepository
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ShutdownService implements OnModuleDestroy {
|
||||||
|
async onModuleDestroy() {
|
||||||
|
await dbConnector.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
GraphQLModule.forRoot<ApolloDriverConfig>({
|
GraphQLModule.forRoot<ApolloDriverConfig>({
|
||||||
|
@ -19,6 +55,14 @@ import { CitationsResolver } from './services/reputation/components/citation/cit
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService, MemberResolver, PostResolver, CitationsResolver],
|
providers: [
|
||||||
|
AppService,
|
||||||
|
StartupService,
|
||||||
|
ShutdownService,
|
||||||
|
MemberResolver,
|
||||||
|
PostResolver,
|
||||||
|
CitationsResolver
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|
|
@ -110,6 +110,7 @@ describe('Rep Service Citation - Unit Tests', () => {
|
||||||
|
|
||||||
describe ('Rep Service Citation - Integration Tests', () => {
|
describe ('Rep Service Citation - Integration Tests', () => {
|
||||||
|
|
||||||
|
process.env.TEST_SUITE = "Rep Service Citation - Integration Tests";
|
||||||
let testServer: ApolloTestServer;
|
let testServer: ApolloTestServer;
|
||||||
|
|
||||||
let createUser1 = `createMember(id: "testingUser1") { id }`;
|
let createUser1 = `createMember(id: "testingUser1") { id }`;
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { DatabaseConnector, Neo4jRepo } from "src/util/database-connector"
|
||||||
|
|
||||||
|
|
||||||
|
export class MemberRepository implements Neo4jRepo {
|
||||||
|
|
||||||
|
private static instance: MemberRepository;
|
||||||
|
private static connector: DatabaseConnector;
|
||||||
|
|
||||||
|
private constructor() { }
|
||||||
|
|
||||||
|
public getInstance() { return MemberRepository.getInstance(); }
|
||||||
|
public static getInstance() {
|
||||||
|
if (!MemberRepository.instance) MemberRepository.instance = new MemberRepository();
|
||||||
|
return MemberRepository.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setConnector(connector: DatabaseConnector) { MemberRepository.setConnector(connector); }
|
||||||
|
public static setConnector(connector: DatabaseConnector) {
|
||||||
|
MemberRepository.connector = connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -76,6 +76,7 @@ describe('Rep Service Member - Unit Tests', () => {
|
||||||
|
|
||||||
describe('Rep Service Member - Integration Tests', () => {
|
describe('Rep Service Member - Integration Tests', () => {
|
||||||
|
|
||||||
|
process.env.TEST_SUITE = "Rep Service Member - Integration Tests";
|
||||||
let testServer: ApolloTestServer;
|
let testServer: ApolloTestServer;
|
||||||
|
|
||||||
let createUser1 = `createMember(id: "testingUser1") { id }`;
|
let createUser1 = `createMember(id: "testingUser1") { id }`;
|
||||||
|
|
|
@ -170,6 +170,7 @@ describe('Rep Service Post - Unit Tests', () => {
|
||||||
|
|
||||||
describe('Rep Service Post - Integration Tests', () => {
|
describe('Rep Service Post - Integration Tests', () => {
|
||||||
|
|
||||||
|
process.env.TEST_SUITE = "Rep Service Post - Integration Tests";
|
||||||
let testServer: ApolloTestServer;
|
let testServer: ApolloTestServer;
|
||||||
|
|
||||||
let createUser1 = `createMember(id: "testingUser1") { id }`;
|
let createUser1 = `createMember(id: "testingUser1") { id }`;
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
import neo4j, { Driver } from 'neo4j-driver';
|
||||||
|
import { Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
export interface DBOptions {
|
||||||
|
host: string;
|
||||||
|
port: string;
|
||||||
|
user: string;
|
||||||
|
pass: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Neo4jRepo {
|
||||||
|
getInstance(): Neo4jRepo;
|
||||||
|
setConnector(connector: DatabaseConnector): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DatabaseConnector {
|
||||||
|
|
||||||
|
private driver: Driver;
|
||||||
|
private logger: Logger
|
||||||
|
|
||||||
|
constructor(logger: Logger) {
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect(options: DBOptions, repos: Neo4jRepo[]) {
|
||||||
|
const connectionString = `neo4j+s://${options.host}:${options.port}`;
|
||||||
|
this.logger.log(`Connection: ${connectionString}`);
|
||||||
|
const authentication = neo4j.auth.basic(options.user, options.pass);
|
||||||
|
try {
|
||||||
|
this.driver = neo4j.driver(connectionString, authentication);
|
||||||
|
const info = await this.driver.getServerInfo();
|
||||||
|
if (!info) throw new Error('Unable to retrieve database info');
|
||||||
|
this.logger.log(`Successfully connected to ${info.agent}`);
|
||||||
|
for (const repo of repos) {
|
||||||
|
this.logger.log(`Attaching to ${(<any>repo).name}`);
|
||||||
|
repo.getInstance().setConnector(this);
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
this.logger.error(`Failed to connect to Database - ${error.message}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
this.logger.log('Closing database connection...');
|
||||||
|
if (this.driver) await this.driver.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
async runQuery(query: string, parameters: any = {}) {
|
||||||
|
const session = this.driver.session();
|
||||||
|
try {
|
||||||
|
const result = await session.writeTransaction(tx => tx.run(query, parameters));
|
||||||
|
return result.records;
|
||||||
|
} finally {
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue