Skip to content

Commit c319b3a

Browse files
feat: Add support for multiple databases (#53)
Co-authored-by: Kia King Ishii <kia.king.08@gmail.com>
1 parent dc20a72 commit c319b3a

File tree

10 files changed

+103
-50
lines changed

10 files changed

+103
-50
lines changed

src/connection/Connection.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Store } from 'vuex'
22
import { Element, Elements } from '../data/Data'
3+
import { Database } from '@/database/Database'
34

45
export interface ConnectionNamespace {
56
connection: string
@@ -25,9 +26,9 @@ export class Connection {
2526
/**
2627
* Create a new connection instance.
2728
*/
28-
constructor(store: Store<any>, entity: string) {
29-
this.store = store
30-
this.connection = store.$database.connection
29+
constructor(database: Database, entity: string) {
30+
this.store = database.store
31+
this.connection = database.connection
3132
this.entity = entity
3233
}
3334

src/database/Database.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export class Database {
8989

9090
if (attr instanceof Relation) {
9191
attr.getRelateds().forEach((m) => {
92-
this.register(m.$setStore(this.store))
92+
this.register(m.$setDatabase(this))
9393
})
9494
}
9595
}

src/interpreter/Interpreter.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { normalize, schema as Normalizr } from 'normalizr'
2-
import { Store } from 'vuex'
32
import { isArray, isEmpty } from '../support/Utils'
43
import { Element, NormalizedData } from '../data/Data'
54
import { Model } from '../model/Model'
5+
import { Database } from '@/database/Database'
66

77
export class Interpreter<M extends Model> {
88
/**
9-
* The store instance.
9+
* The database instance.
1010
*/
11-
store: Store<any>
11+
database: Database
1212

1313
/**
1414
* The model object.
@@ -18,8 +18,8 @@ export class Interpreter<M extends Model> {
1818
/**
1919
* Create a new Interpreter instance.
2020
*/
21-
constructor(store: Store<any>, model: M) {
22-
this.store = store
21+
constructor(database: Database, model: M) {
22+
this.database = database
2323
this.model = model
2424
}
2525

@@ -43,6 +43,6 @@ export class Interpreter<M extends Model> {
4343
* Get the schema from the database.
4444
*/
4545
private getSchema(): Normalizr.Entity {
46-
return this.store.$database.getSchema(this.model.$entity())
46+
return this.database.getSchema(this.model.$entity())
4747
}
4848
}

src/model/Model.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Store } from 'vuex'
21
import { isNullish, isArray, assert } from '../support/Utils'
32
import { Element, Item, Collection } from '../data/Data'
43
import { Query } from '../query/Query'
@@ -14,6 +13,7 @@ import { HasOne } from './attributes/relations/HasOne'
1413
import { BelongsTo } from './attributes/relations/BelongsTo'
1514
import { HasMany } from './attributes/relations/HasMany'
1615
import { HasManyBy } from './attributes/relations/HasManyBy'
16+
import { Database } from '@/database/Database'
1717

1818
export type ModelFields = Record<string, Attribute>
1919
export type ModelSchemas = Record<string, ModelFields>
@@ -55,10 +55,10 @@ export class Model {
5555
protected static booted: Record<string, boolean> = {}
5656

5757
/**
58-
* The store instance.
58+
* The database instance.
5959
*/
6060
@NonEnumerable
61-
protected _store!: Store<any>
61+
protected _database!: Database
6262

6363
/**
6464
* Create a new model instance.
@@ -243,23 +243,23 @@ export class Model {
243243
}
244244

245245
/**
246-
* Get the store instance.
246+
* Get the database instance.
247247
*/
248-
$store(): Store<any> {
249-
assert(this._store !== undefined, [
248+
$database(): Database {
249+
assert(this._database !== undefined, [
250250
'A Vuex Store instance is not injected into the model instance.',
251251
'You might be trying to instantiate the model directly. Please use',
252252
'`repository.make` method to create a new model instance.'
253253
])
254254

255-
return this._store
255+
return this._database
256256
}
257257

258258
/**
259-
* Set the store instance.
259+
* Set the database instance.
260260
*/
261-
$setStore(store: Store<any>): this {
262-
this._store = store
261+
$setDatabase(database: Database): this {
262+
this._database = database
263263

264264
return this
265265
}
@@ -294,7 +294,7 @@ export class Model {
294294
const self = this.$self()
295295
const model = new self(attributes, options) as this
296296

297-
model.$setStore(this.$store())
297+
model.$setDatabase(this.$database())
298298

299299
return model
300300
}
@@ -303,7 +303,7 @@ export class Model {
303303
* Create a new query instance.
304304
*/
305305
$query(): Query<this> {
306-
return new Query(this.$store(), this)
306+
return new Query(this.$database(), this)
307307
}
308308

309309
/**

src/query/Query.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Store } from 'vuex'
21
import {
32
isArray,
43
isFunction,
@@ -30,6 +29,7 @@ import {
3029
EagerLoadConstraint,
3130
PersistMethod
3231
} from './Options'
32+
import { Database } from '@/database/Database'
3333

3434
export interface CollectionPromises {
3535
indexes: string[]
@@ -38,9 +38,9 @@ export interface CollectionPromises {
3838

3939
export class Query<M extends Model = Model> {
4040
/**
41-
* The store instance.
41+
* The database instance.
4242
*/
43-
protected store: Store<any>
43+
database: Database
4444

4545
/**
4646
* The model object.
@@ -85,26 +85,29 @@ export class Query<M extends Model = Model> {
8585
/**
8686
* Create a new query instance.
8787
*/
88-
constructor(store: Store<any>, model: M) {
89-
this.store = store
88+
constructor(database: Database, model: M) {
89+
this.database = database
9090
this.model = model
9191

92-
this.interpreter = new Interpreter(store, model)
93-
this.connection = new Connection(store, model.$entity())
92+
this.interpreter = new Interpreter(database, model)
93+
this.connection = new Connection(database, model.$entity())
9494
}
9595

9696
/**
9797
* Create a new query instance for the given model.
9898
*/
9999
protected newQuery(model: string): Query<Model> {
100-
return new Query(this.store, this.store.$database.getModel(model))
100+
return new Query(this.database, this.database.getModel(model))
101101
}
102102

103103
/**
104104
* Create a new query instance from the given relation.
105105
*/
106106
protected newQueryForRelation(relation: Relation): Query<Model> {
107-
return new Query(this.store, relation.getRelated().$setStore(this.store))
107+
return new Query(
108+
this.database,
109+
relation.getRelated().$setDatabase(this.database)
110+
)
108111
}
109112

110113
/**

src/repository/Repository.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Store } from 'vuex'
21
import { Constructor } from '../types'
32
import { assert } from '../support/Utils'
43
import { Element, Item, Collection, Collections } from '../data/Data'
@@ -12,6 +11,7 @@ import {
1211
OrderBy,
1312
EagerLoadConstraint
1413
} from '../query/Options'
14+
import { Database } from '@/database/Database'
1515

1616
export class Repository<M extends Model = Model> {
1717
/**
@@ -22,9 +22,9 @@ export class Repository<M extends Model = Model> {
2222
static _isRepository: boolean = true
2323

2424
/**
25-
* The store instance.
25+
* The database instance.
2626
*/
27-
protected store: Store<any>
27+
database: Database
2828

2929
/**
3030
* The model instance.
@@ -39,8 +39,8 @@ export class Repository<M extends Model = Model> {
3939
/**
4040
* Create a new Repository instance.
4141
*/
42-
constructor(store: Store<any>) {
43-
this.store = store
42+
constructor(database: Database) {
43+
this.database = database
4444
}
4545

4646
/**
@@ -49,7 +49,7 @@ export class Repository<M extends Model = Model> {
4949
initialize(model?: ModelConstructor<M>): this {
5050
// If there's a model passed in, just use that and return immediately.
5151
if (model) {
52-
this.model = model.newRawInstance().$setStore(this.store)
52+
this.model = model.newRawInstance().$setDatabase(this.database)
5353

5454
return this
5555
}
@@ -59,7 +59,7 @@ export class Repository<M extends Model = Model> {
5959
// In this case, we'll check if the user has set model to the `use`
6060
// property and instantiate that.
6161
if (this.use) {
62-
this.model = (this.use.newRawInstance() as M).$setStore(this.store)
62+
this.model = (this.use.newRawInstance() as M).$setDatabase(this.database)
6363

6464
return this
6565
}
@@ -89,14 +89,14 @@ export class Repository<M extends Model = Model> {
8989
repo<M extends Model>(model: Constructor<M>): Repository<M>
9090
repo<R extends Repository<any>>(repository: Constructor<R>): R
9191
repo(modelOrRepository: any): any {
92-
return this.store.$repo(modelOrRepository)
92+
return this.database.store.$repo(modelOrRepository)
9393
}
9494

9595
/**
9696
* Create a new Query instance.
9797
*/
9898
query(): Query<M> {
99-
return new Query(this.store, this.getModel())
99+
return new Query(this.database, this.getModel())
100100
}
101101

102102
/**

src/store/Store.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,20 @@ function mixin(store: Store<any>, options: FilledInstallOptions): void {
4646
function createDatabase(
4747
store: Store<any>,
4848
options: FilledInstallOptions
49-
): void {
49+
): Database {
5050
const database = new Database()
5151
.setStore(store)
5252
.setConnection(options.namespace)
5353

5454
store.$database = database
55+
56+
if (!store.$databases) {
57+
store.$databases = {}
58+
}
59+
60+
store.$databases[database.connection] = database
61+
62+
return database
5563
}
5664

5765
/**
@@ -76,13 +84,26 @@ function startDatabase(store: Store<any>): void {
7684
* Mixin repo function to the store.
7785
*/
7886
function mixinRepoFunction(store: Store<any>): void {
79-
store.$repo = function (modelOrRepository: any): any {
87+
store.$repo = function (modelOrRepository: any, connection?: string): any {
88+
let database: Database
89+
90+
if (connection) {
91+
if (!(connection in store.$databases)) {
92+
database = createDatabase(store, { namespace: connection })
93+
database.start()
94+
} else {
95+
database = store.$databases[connection]
96+
}
97+
} else {
98+
database = store.$database
99+
}
100+
80101
const repository = modelOrRepository._isRepository
81102
? new modelOrRepository(this).initialize()
82-
: new Repository(this).initialize(modelOrRepository)
103+
: new Repository(database).initialize(modelOrRepository)
83104

84105
try {
85-
store.$database.register(repository.getModel())
106+
database.register(repository.getModel())
86107
} catch (e) {
87108
} finally {
88109
return repository

src/types/vuex.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,26 @@ import { Repository } from '../repository/Repository'
66
declare module 'vuex/types/index' {
77
interface Store<S> {
88
/**
9-
* The database instance.
9+
* The default database instance.
1010
*/
1111
$database: Database
1212

13+
/**
14+
* Mapping of databases keyed on connection
15+
*/
16+
$databases: { [key: string]: Database }
17+
1318
/**
1419
* Get a new Repository instance for the given model.
1520
*/
16-
$repo<M extends Model>(model: Constructor<M>): Repository<M>
17-
$repo<R extends Repository<any>>(repository: Constructor<R>): R
21+
$repo<M extends Model>(
22+
model: Constructor<M>,
23+
connection?: string
24+
): Repository<M>
25+
26+
$repo<R extends Repository>(
27+
repository: Constructor<R>,
28+
connection?: string
29+
): R
1830
}
1931
}

test/unit/model/Model.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ describe('unit/model/Model', () => {
66
}
77

88
it('throws when accessing the store but it is not injected', () => {
9-
expect(() => new User().$store()).toThrow()
9+
expect(() => new User().$database()).toThrow()
1010
})
1111
})

test/unit/repository/Repository.spec.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,26 @@ describe('unit/repository/Repository', () => {
2222
const user = store.$repo(User).make()
2323

2424
expect(user).toBeInstanceOf(User)
25-
expect(user.$store()).toBe(store)
25+
expect(user.$database()).toBe(store.$database)
2626
assertModel(user, { id: null, name: 'John Doe' })
2727
})
2828

29+
it('creates a new model instance in a new database', () => {
30+
const store = createStore()
31+
32+
const connection = 'test_namespace'
33+
const user = store.$repo(User, connection).make()
34+
35+
expect(user).toBeInstanceOf(User)
36+
expect(user.$database()).toBe(store.$databases[connection])
37+
expect(user.$database().started).toBe(true)
38+
assertModel(user, { id: null, name: 'John Doe' })
39+
40+
// Fetches the same atabase on 2nd call.
41+
const user2 = store.$repo(User, connection).make()
42+
assertModel(user2, { id: null, name: 'John Doe' })
43+
})
44+
2945
it('creates a new model instance with default values', () => {
3046
const store = createStore()
3147

@@ -35,7 +51,7 @@ describe('unit/repository/Repository', () => {
3551
})
3652

3753
expect(user).toBeInstanceOf(User)
38-
expect(user.$store()).toBe(store)
54+
expect(user.$database()).toBe(store.$database)
3955
assertModel(user, { id: 1, name: 'Jane Doe' })
4056
})
4157

0 commit comments

Comments
 (0)