Skip to content

Commit

Permalink
Database: Generates unique reproducible md5 hash
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed Apr 14, 2021
1 parent f225af7 commit 10272b4
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 7 deletions.
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export default {
preset: 'ts-jest',
testTimeout: 60000,
moduleNameMapper: {
'^@mswjs/data(.*)': '<rootDir>/$1',
},
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@
"typescript": "^4.2.4"
},
"dependencies": {
"@types/md5": "^2.3.0",
"@types/pluralize": "^0.0.29",
"@types/uuid": "^8.3.0",
"date-fns": "^2.20.0",
"graphql": "^15.5.0",
"md5": "^2.3.0",
"pluralize": "^8.0.0",
"strict-event-emitter": "^0.2.0",
"uuid": "^8.3.1"
Expand Down
26 changes: 23 additions & 3 deletions src/db/Database.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import md5 from 'md5'
import { StrictEventEmitter } from 'strict-event-emitter'
import { EntityInstance, ModelDictionary, PrimaryKeyType } from '../glossary'

Expand All @@ -7,6 +8,7 @@ type Models<Dictionary extends ModelDictionary> = Record<
>

export type DatabaseMethodToEventFn<Method extends (...args: any[]) => any> = (
id: string,
...args: Parameters<Method>
) => void

Expand All @@ -16,7 +18,10 @@ export interface DatabaseEventsMap {
delete: DatabaseMethodToEventFn<Database<any>['delete']>
}

let callOrder = 0

export class Database<Dictionary extends ModelDictionary> {
public id: string
public events: StrictEventEmitter<DatabaseEventsMap>
private models: Models<ModelDictionary>

Expand All @@ -29,6 +34,21 @@ export class Database<Dictionary extends ModelDictionary> {
},
{},
)

callOrder++
this.id = this.generateId()
}

/**
* Generates a unique MD5 hash based on the database
* module location and invocation order. Used to reproducibly
* identify a database instance among sibling instances.
*/
private generateId() {
const { stack } = new Error()
const callFrame = stack?.split('\n')[4]
const salt = `${callOrder}-${callFrame?.trim()}`
return md5(salt)
}

getModel(name: string) {
Expand All @@ -43,7 +63,7 @@ export class Database<Dictionary extends ModelDictionary> {
const primaryKey =
customPrimaryKey || (entity[entity.__primaryKey] as string)

this.events.emit('create', modelName, entity, customPrimaryKey)
this.events.emit('create', this.id, modelName, entity, customPrimaryKey)

return this.getModel(modelName).set(primaryKey, entity)
}
Expand All @@ -61,7 +81,7 @@ export class Database<Dictionary extends ModelDictionary> {
}

this.create(modelName, nextEntity, nextPrimaryKey as string)
this.events.emit('update', modelName, prevEntity, nextEntity)
this.events.emit('update', this.id, modelName, prevEntity, nextEntity)
}

has(modelName: string, primaryKey: PrimaryKeyType) {
Expand All @@ -74,7 +94,7 @@ export class Database<Dictionary extends ModelDictionary> {

delete(modelName: string, primaryKey: PrimaryKeyType) {
this.getModel(modelName).delete(primaryKey)
this.events.emit('delete', modelName, primaryKey)
this.events.emit('delete', this.id, modelName, primaryKey)
}

listEntities(modelName: string) {
Expand Down
9 changes: 6 additions & 3 deletions test/db/events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ test('emits the "create" event when a new entity is created', (done) => {
},
})

db.events.on('create', (modelName, entity, primaryKey) => {
db.events.on('create', (id, modelName, entity, primaryKey) => {
expect(id).toEqual(db.id)
expect(modelName).toEqual('user')
expect(entity).toEqual({
__type: 'user',
Expand All @@ -31,7 +32,8 @@ test('emits the "update" event when an existing entity is updated', (done) => {
},
})

db.events.on('update', (modelName, prevEntity, nextEntity) => {
db.events.on('update', (id, modelName, prevEntity, nextEntity) => {
expect(id).toEqual(db.id)
expect(modelName).toEqual('user')
expect(prevEntity).toEqual({
__type: 'user',
Expand Down Expand Up @@ -67,7 +69,8 @@ test('emits the "delete" event when an existing entity is deleted', (done) => {
},
})

db.events.on('delete', (modelName, primaryKey) => {
db.events.on('delete', (id, modelName, primaryKey) => {
expect(id).toEqual(db.id)
expect(modelName).toEqual('user')
expect(primaryKey).toEqual('abc-123')
done()
Expand Down
28 changes: 27 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,13 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==

"@types/md5@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.0.tgz#3b6a623091160f4dc75be3173e25f2110dc3fa1f"
integrity sha512-556YJ7ejzxIqSSxzyGGpctuZOarNZJt/zlEkhmmDc1f/slOEANHuwu2ZX7YaZ40rMiWoxt8GvAhoDpW1cmSy6A==
dependencies:
"@types/node" "*"

"@types/mime@^1":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
Expand Down Expand Up @@ -1323,6 +1330,11 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==

[email protected]:
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=

chokidar@^3.4.2:
version "3.4.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
Expand Down Expand Up @@ -1545,6 +1557,11 @@ cross-spawn@^7.0.0:
shebang-command "^2.0.0"
which "^2.0.1"

[email protected]:
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=

cssom@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
Expand Down Expand Up @@ -2445,7 +2462,7 @@ is-binary-path@~2.1.0:
dependencies:
binary-extensions "^2.0.0"

is-buffer@^1.1.5:
is-buffer@^1.1.5, is-buffer@~1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
Expand Down Expand Up @@ -3246,6 +3263,15 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"

md5@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==
dependencies:
charenc "0.0.2"
crypt "0.0.2"
is-buffer "~1.1.6"

[email protected]:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
Expand Down

0 comments on commit 10272b4

Please sign in to comment.