Skip to content

Commit

Permalink
added provider agnostic database module (#19)
Browse files Browse the repository at this point in the history
* updated jellyfish-api-* versions
* added database module abstraction
* e2e test will use database memory provider by default
* added missing WHALE_DATABASE_PROVIDER for sanity testing
  • Loading branch information
fuxingloh authored Apr 27, 2021
1 parent d6a0fa4 commit 3aa0613
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 1 deletion.
5 changes: 4 additions & 1 deletion apps/whale-api/src/app.configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ export default (): any => ({
defid: {
url: process.env.WHALE_DEFID_URL
},
network: process.env.WHALE_NETWORK
network: process.env.WHALE_NETWORK,
database: {
provider: process.env.WHALE_DATABASE_PROVIDER
}
})
2 changes: 2 additions & 0 deletions apps/whale-api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ApiModule } from '@src/module.api'
import { DeFiDModule } from '@src/module.defid'
import configuration from '@src/app.configuration'
import { HealthModule } from '@src/module.health'
import { DatabaseModule } from '@src/module.database'

@Module({
imports: [
Expand All @@ -14,6 +15,7 @@ import { HealthModule } from '@src/module.health'
load: [configuration]
}),
ScheduleModule.forRoot(),
DatabaseModule,
DeFiDModule,
HealthModule,
ApiModule
Expand Down
7 changes: 7 additions & 0 deletions apps/whale-api/src/module.database/database.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export abstract class Database {
// TODO(fuxingloh): remove temporary implementation

abstract get (key: string): any

abstract put (key: string, data: any): void
}
69 changes: 69 additions & 0 deletions apps/whale-api/src/module.database/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Test, TestingModule } from '@nestjs/testing'
import { ConfigModule } from '@nestjs/config'
import { DatabaseModule } from '@src/module.database'
import { Database } from '@src/module.database/database'
import { MemoryDatabase } from '@src/module.database/provider.memory/memory.database'

describe('memory module', () => {
let app: TestingModule
let database: Database

beforeEach(async () => {
app = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [() => ({
database: {
provider: 'memory'
}
})]
}),
DatabaseModule
]
}).compile()

database = app.get<Database>(Database)
})

it('dynamically injected database should be memory database', () => {
expect(database instanceof MemoryDatabase).toBe(true)
})

it('should be a singleton module', () => {
expect(app.get<Database>(Database).get('1')).toBeFalsy()
expect(app.get<Database>(Database).get('2')).toBeFalsy()

app.get<Database>(Database).put('1', 'a-1')
app.get<Database>(Database).put('2', { b: 2 })

expect(app.get<Database>(Database).get('1')).toBe('a-1')
expect(app.get<Database>(Database).get('2')).toEqual({
b: 2
})
})
})

describe('invalid module', () => {
it('should fail module instantiation as database provider is invalid', async () => {
const initModule = async (): Promise<void> => {
await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [() => ({
database: {
provider: 'invalid'
}
})]
}),
DatabaseModule
]
}).compile()
}

await expect(initModule)
.rejects
.toThrow('bootstrapping error: invalid database.provider - invalid')
})
})
42 changes: 42 additions & 0 deletions apps/whale-api/src/module.database/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ModuleRef } from '@nestjs/core'
import { Global, Module } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { FactoryProvider } from '@nestjs/common/interfaces/modules/provider.interface'
import { Database } from '@src/module.database/database'
import { MemoryDatabaseModule } from '@src/module.database/provider.memory'
import { MemoryDatabase } from '@src/module.database/provider.memory/memory.database'

/**
* Runtime DatabaseProvider, dynamically inject a database type based on config in the database.provider.
* @see Database
* @see MemoryDatabase
*/
const DatabaseProvider: FactoryProvider = {
provide: Database,
useFactory: async (configService: ConfigService, moduleRef: ModuleRef) => {
const provider = configService.get<string>('database.provider', '')
switch (provider) {
case 'memory':
return await moduleRef.create(MemoryDatabase)
default:
throw new Error(`bootstrapping error: invalid database.provider - ${provider}`)
}
},
inject: [ConfigService, ModuleRef]
}

/**
* DeFi Whale Database Module for service agnostic storage layer.
*/
@Global()
@Module({
imports: [
MemoryDatabaseModule
],
providers: [
DatabaseProvider
],
exports: [Database]
})
export class DatabaseModule {
}
13 changes: 13 additions & 0 deletions apps/whale-api/src/module.database/provider.memory/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common'
import { MemoryDatabase } from '@src/module.database/provider.memory/memory.database'

/**
* Provides the memory database module where all data is stored in memory in NodeJS
*/
@Module({
providers: [
MemoryDatabase
]
})
export class MemoryDatabaseModule {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Database } from '@src/module.database/database'

export class MemoryDatabase extends Database {
// TODO(fuxingloh): remove temporary implementation

private readonly records: Record<string, any> = {}

get (key: string): any {
return this.records[key]
}

put (key: string, data: any): void {
this.records[key] = data
}
}

0 comments on commit 3aa0613

Please sign in to comment.