Skip to content

Instantly share code, notes, and snippets.

@nclsndr
Created September 3, 2018 14:23
Show Gist options
  • Save nclsndr/c47fb601cef289b5ec5d621ea9a41608 to your computer and use it in GitHub Desktop.
Save nclsndr/c47fb601cef289b5ec5d621ea9a41608 to your computer and use it in GitHub Desktop.
Mongo Document manager
/* ------------------------------------------
DataCollection
--------------------------------------------- */
class DataCollection {
constructor(v) {
this.value = v || []
}
override(v) {
this.value = v.slice(0)
}
exec(fn) {
this.value = fn(this.value)
}
get() {
return this.value
}
}
module.exports = DataCollection
/* ------------------------------------------
DataMap
--------------------------------------------- */
class DataMap {
constructor(v) {
this.value = v || {}
}
set(k, v) {
this.value = { ...this.value, [k]: v }
}
remove(k) {
this.value = Object
.entries(this.value)
.reduce((acc, [a,b]) => ({
...acc,
...k === a ? {} : { [a]: b }
}), {})
}
merge(v) {
this.value = { ...this.value, ...v }
}
get() {
return this.value
}
}
module.exports = DataMap
/* ------------------------------------------
Document
--------------------------------------------- */
const { ObjectId } = require('mongodb')
const capitalize = require('lodash/capitalize')
const { get } = require('../../services/db')
const HooksManager = require('./HooksManager')
const DataCollection = require('./DataCollection')
const DataMap = require('./DataMap')
const Schema = require('./Schema')
// TODO @Nico : Delete
// TODO @Nico : Set index
class Document {
constructor(collection, schema) {
this.schema = new Schema(schema)
this.collection = collection
this.schema = {}
this.hooks = {
beforeCreate: new HooksManager('beforeCreate'),
afterCreate: new HooksManager('afterCreate'),
beforeUpdate: new HooksManager('beforeUpdate'),
afterUpdate: new HooksManager('afterUpdate'),
beforeFind: new HooksManager('beforeFind'),
afterFind: new HooksManager('afterFind'),
afterFindById: new HooksManager('afterFindById'),
}
}
registerHookDomain(name) {
const pre = `before${capitalize(name)}`
this.hooks[pre] = new HooksManager(pre)
const post = `after${capitalize(name)}`
this.hooks[post] = new HooksManager(post)
}
registerHook(hook, signature, promise) {
const hookNames = Object.keys(this.hooks)
if (!hookNames.includes(hook)) throw new Error(`Document do not contains "${hook}" hook`)
this.hooks[hook].register(signature, promise)
}
removeHook(hook, signature) {
const hookNames = Object.keys(this.hooks)
if (!hookNames.includes(hook)) throw new Error(`Document do not contains "${hook}" hook`)
this.hooks[hook].remove(signature)
}
async create(data) {
const db = await get()
const preHooksResult = new DataMap(data)
await this.hooks.beforeCreate.run(data, preHooksResult)
const doc = await db
.collection(this.collection)
.insertOne(preHooksResult.get())
.then(op => op.ops[0])
const postHooksResult = new DataMap(doc)
await this.hooks.afterCreate.run(doc, postHooksResult)
return postHooksResult.get()
}
async update(filter, upgrade, options) {
const db = await get()
const preHooksResult = new DataMap(upgrade)
await this.hooks.beforeCreate.run(upgrade, preHooksResult)
const doc = await db
.collection(this.collection)
.findOneAndUpdate(filter, preHooksResult, {...options, new: true })
.then(op => op.value)
const postHooksResult = new DataMap(doc)
await this.hooks.afterUpdate.run(doc, postHooksResult)
return postHooksResult.get()
}
async find(query, options) {
const db = await get()
const preHooksResult = new DataMap(query)
await this.hooks.beforeFind.run(query, preHooksResult)
const docs = await db
.collection(this.collection)
.find(preHooksResult.get(), options)
.toArray()
const postHooksResult = new DataCollection(docs)
await this.hooks.afterFind.run(docs, postHooksResult)
return postHooksResult.get()
}
async findById(id, options) {
const db = await get()
const docs = await db
.collection(this.collection)
.find({ _id: ObjectId(id) }, options)
.toArray()
if (docs.length === 0) return null
const doc = docs.reduce((acc, c) => ({ ...acc, ...c }), {})
const postHooksResult = new DataMap(doc)
await this.hooks.afterFindById.run(doc, postHooksResult)
return postHooksResult.get()
}
async collection() {
const db = await get()
return db.collection(this.collection)
}
}
module.exports = Document
/* ------------------------------------------
HooksManager
--------------------------------------------- */
class HooksManager {
constructor(name) {
this.name = name
this.fns = []
}
register(signature, promise) {
this.fns.forEach(h => {
if (h.signature === signature) throw new Error(`Signature ${signature} is already registered`)
})
this.fns.push({ signature, promise })
}
remove(signature) {
const index = this.fns.reduce((acc, c, i) => c.signature === signature ? i : acc, -1)
this.fns.splice(index, 1)
}
async run(...args) {
const [...inputs, output] = args
if (this.fns.length === 0) return Promise.resolve(output)
for (let f of this.fns) {
await f.promise(...inputs, output)
}
}
}
module.exports = HooksManager
/* ------------------------------------------
Schema manager
--------------------------------------------- */
class Schema {
constructor({ shape }) {
this.shape = shape
}
generate(input) {
const final = Object
.entries(this.shape)
// .()
return {
updatedAt: null,
createdAt: new Date()
}
}
}
module.exports = Schema
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment