database-connector for neo4j
This commit is contained in:
parent
f83eac3ccc
commit
b142a1eded
|
@ -32,4 +32,7 @@ lerna-debug.log*
|
|||
!.vscode/settings.json
|
||||
!.vscode/tasks.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/platform-express": "^10.0.0",
|
||||
"apollo-server-core": "^3.12.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"graphql": "^16.6.0",
|
||||
"neo4j-driver": "^5.11.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.8.1",
|
||||
"ts-morph": "^19.0.0"
|
||||
|
@ -3582,7 +3584,6 @@
|
|||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
@ -4346,6 +4347,17 @@
|
|||
"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": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
|
@ -5465,7 +5477,6 @@
|
|||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
@ -6844,6 +6855,62 @@
|
|||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"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": {
|
||||
"version": "3.1.1",
|
||||
"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/platform-express": "^10.0.0",
|
||||
"apollo-server-core": "^3.12.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"graphql": "^16.6.0",
|
||||
"neo4j-driver": "^5.11.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.8.1",
|
||||
"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 { AppService } from './app.service';
|
||||
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 { 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({
|
||||
imports: [
|
||||
GraphQLModule.forRoot<ApolloDriverConfig>({
|
||||
|
@ -19,6 +55,14 @@ import { CitationsResolver } from './services/reputation/components/citation/cit
|
|||
}),
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService, MemberResolver, PostResolver, CitationsResolver],
|
||||
providers: [
|
||||
AppService,
|
||||
StartupService,
|
||||
ShutdownService,
|
||||
MemberResolver,
|
||||
PostResolver,
|
||||
CitationsResolver
|
||||
],
|
||||
})
|
||||
|
||||
export class AppModule {}
|
||||
|
|
|
@ -110,6 +110,7 @@ describe('Rep Service Citation - Unit Tests', () => {
|
|||
|
||||
describe ('Rep Service Citation - Integration Tests', () => {
|
||||
|
||||
process.env.TEST_SUITE = "Rep Service Citation - Integration Tests";
|
||||
let testServer: ApolloTestServer;
|
||||
|
||||
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', () => {
|
||||
|
||||
process.env.TEST_SUITE = "Rep Service Member - Integration Tests";
|
||||
let testServer: ApolloTestServer;
|
||||
|
||||
let createUser1 = `createMember(id: "testingUser1") { id }`;
|
||||
|
|
|
@ -170,6 +170,7 @@ describe('Rep Service Post - Unit Tests', () => {
|
|||
|
||||
describe('Rep Service Post - Integration Tests', () => {
|
||||
|
||||
process.env.TEST_SUITE = "Rep Service Post - Integration Tests";
|
||||
let testServer: ApolloTestServer;
|
||||
|
||||
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