Quellcode durchsuchen

Generalised CRUD database method creator

Fela Maslen vor 7 Jahren
Ursprung
Commit
6e34ee59c3

+ 92 - 0
src/server/modules/crud.js

@@ -0,0 +1,92 @@
+const joi = require('joi');
+
+const { getObjectId } = require('server/modules/db');
+const { clientError } = require('server/modules/client-error');
+
+function makeInsertDoc(collection, schema) {
+    const schemaRequired = Object.keys(schema)
+        .reduce((items, key) => ({
+            ...items,
+            [key]: schema[key].required()
+        }), {});
+
+    return async (db, info) => {
+        const { error, value } = joi.validate(info, schemaRequired);
+
+        if (error) {
+            throw clientError(error);
+        }
+
+        const dbCollection = await db.collection(collection);
+
+        const result = await dbCollection.insertOne(value);
+
+        try {
+            const { _id, ...dbResult } = result.ops[0];
+
+            return {
+                id: String(_id),
+                ...dbResult
+            };
+
+        } catch (err) {
+            return value;
+        }
+    };
+
+}
+
+function makeModifyDoc(collection, schema) {
+    return async (db, id, info) => {
+        const { error, value } = joi.validate(info, schema);
+
+        if (error) {
+            throw clientError(error);
+        }
+
+        const dbCollection = await db.collection(collection);
+
+        const _id = getObjectId(id);
+
+        await dbCollection.updateOne({ _id }, { $set: value }, {
+            upsert: false,
+            multi: false
+        });
+    };
+}
+
+function makeDeleteDoc(collection) {
+    return async (db, id) => {
+        const dbCollection = await db.collection(collection);
+
+        await dbCollection.deleteOne({ _id: getObjectId(id) });
+    };
+}
+
+function makeGetDocs(collection) {
+    return async db => {
+        const dbCollection = await db.collection(collection);
+
+        const rows = await dbCollection.find({})
+            .toArray();
+
+        return rows.map(({ _id, ...rest }) => ({
+            id: String(_id),
+            ...rest
+        }));
+    };
+}
+
+function makeMongoCrud(collection, schema) {
+    return {
+        insertDoc: makeInsertDoc(collection, schema),
+        modifyDoc: makeModifyDoc(collection, schema),
+        deleteDoc: makeDeleteDoc(collection),
+        getDocs: makeGetDocs(collection)
+    };
+}
+
+module.exports = {
+    makeMongoCrud
+};
+

+ 10 - 1
src/server/modules/db.js

@@ -1,4 +1,12 @@
-const { MongoClient } = require('mongodb');
+const { MongoClient, ObjectID } = require('mongodb');
+
+function getObjectId(id) {
+    if (process.env.NODE_ENV === 'test') {
+        return id;
+    }
+
+    return new ObjectID(id);
+}
 
 async function getDBConnection(config, logger) {
     try {
@@ -20,6 +28,7 @@ async function getDBConnection(config, logger) {
 }
 
 module.exports = {
+    getObjectId,
     getDBConnection
 };
 

+ 8 - 81
src/server/modules/users.js

@@ -1,91 +1,18 @@
 const joi = require('joi');
-const { ObjectID } = require('mongodb');
 
-const { clientError } = require('server/modules/client-error');
+const { makeMongoCrud } = require('server/modules/crud');
 
 const COLLECTION_USERS = 'users';
 
-function getObjectId(id) {
-    if (process.env.NODE_ENV === 'test') {
-        return id;
-    }
-
-    return new ObjectID(id);
-}
-
-async function insertUser(db, info) {
-    const { error, value } = joi.validate(info, {
-        name: joi.string().required(),
-        email: joi.string()
-            .email()
-            .required()
-    });
-
-    if (error) {
-        throw clientError(error);
-    }
-
-    const users = await db.collection(COLLECTION_USERS);
-
-    const result = await users.insert(value);
-
-    try {
-        const { _id, ...userResult } = result.ops[0];
-
-        return {
-            id: String(_id),
-            ...userResult
-        };
-
-    } catch (err) {
-        return value;
-    }
-}
-
-async function modifyUser(db, id, info) {
-    const { error, value } = joi.validate(info, {
-        name: joi.string(),
-        email: joi.string()
-            .email()
-    });
-
-    if (error) {
-        throw clientError(error);
-    }
-
-    const users = await db.collection(COLLECTION_USERS);
-
-    const _id = getObjectId(id);
-
-    await users.updateOne({ _id }, { $set: value }, {
-        upsert: false,
-        multi: false
-    });
-}
-
-async function deleteUser(db, id) {
-    const users = await db.collection(COLLECTION_USERS);
-
-    await users.deleteOne({ _id: getObjectId(id) });
-}
-
-async function getUsers(db) {
-    const users = await db.collection(COLLECTION_USERS);
-
-    const rows = await users.find({})
-        .toArray();
+const SCHEMA_USERS = {
+    name: joi.string(),
+    email: joi.string()
+        .email()
+};
 
-    return rows.map(({ _id, ...rest }) => ({
-        id: String(_id),
-        ...rest
-    }));
-}
+const makeUserCrud = () => makeMongoCrud(COLLECTION_USERS, SCHEMA_USERS);
 
 module.exports = {
-    insertUser,
-    modifyUser,
-    deleteUser,
-    getUsers,
-    COLLECTION_USERS
+    makeUserCrud
 };
 

+ 4 - 4
src/server/routes/annoy.js

@@ -1,8 +1,6 @@
 const moment = require('moment');
 
-const {
-    getUsers
-} = require('server/modules/users');
+const { makeUserCrud } = require('server/modules/users');
 
 const {
     getAllSlackUsersWithNames,
@@ -31,12 +29,14 @@ function getPreviousWeekPeriod(now) {
 }
 
 function annoyUsers(config, db, logger) {
+    const crud = makeUserCrud();
+
     return async (req, res) => {
         const now = moment();
 
         const range = getPreviousWeekPeriod(now);
 
-        const validUsers = await getUsers(db);
+        const validUsers = await crud.getDocs(db);
 
         const validUserEmails = validUsers.map(({ email }) => email.toLowerCase());
 

+ 7 - 8
src/server/routes/employees.js

@@ -1,39 +1,38 @@
 const { Router } = require('express');
 
 const {
-    insertUser,
-    modifyUser,
-    deleteUser,
-    getUsers
+    makeUserCrud
 } = require('server/modules/users');
 
 function employees(config, db) {
     const router = new Router();
 
+    const crud = makeUserCrud();
+
     router.get('/', async (req, res) => {
 
-        const users = await getUsers(db);
+        const users = await crud.getDocs(db);
 
         res.json({ employees: users });
     });
 
     router.post('/', async (req, res) => {
 
-        const result = await insertUser(db, req.body);
+        const result = await crud.insertDoc(db, req.body);
 
         res.json(result);
     });
 
     router.put('/:id', async (req, res) => {
 
-        await modifyUser(db, req.params.id, req.body);
+        await crud.modifyDoc(db, req.params.id, req.body);
 
         res.json({ ok: true });
     });
 
     router.delete('/:id', async (req, res) => {
 
-        await deleteUser(db, req.params.id);
+        await crud.deleteDoc(db, req.params.id);
 
         res.json({ ok: true });
     });

+ 57 - 29
test/server/modules/users.spec.js → test/server/modules/crud.spec.js

@@ -1,33 +1,40 @@
 /* eslint-disable no-unused-expressions */
 
 const { expect } = require('chai');
-
 const mongoMocker = require('mongo-mocker');
 
+const joi = require('joi');
+
 const {
-    insertUser,
-    modifyUser,
-    deleteUser,
-    getUsers,
-    COLLECTION_USERS
-} = require('server/modules/users');
-
-describe('API - Users module', () => {
+    makeMongoCrud
+} = require('server/modules/crud');
+
+describe('API - Crud module', () => {
     let db = null;
+    let crud = null;
+
+    const schema = {
+        name: joi.string(),
+        email: joi.string().email()
+    };
 
     const testUsers = [
         { name: 'John Doe', email: 'john.doe@mubaloo.com' },
         { name: 'Jill Doe', email: 'jill.doe@mubaloo.com' }
     ];
 
+    const collection = 'users';
+
     before(() => {
         db = mongoMocker('mongoDriver/path', {
-            [COLLECTION_USERS]: testUsers
+            [collection]: testUsers
         });
+
+        crud = makeMongoCrud(collection, schema);
     });
 
     beforeEach(async () => {
-        const users = await db.collection(COLLECTION_USERS);
+        const users = await db.collection(collection);
 
         await Promise.all(testUsers.map(user => users.insert(user)));
     });
@@ -36,9 +43,14 @@ describe('API - Users module', () => {
         db.mock.clearAll();
     });
 
-    describe('insertUser', () => {
-        it('should insert an employee into the users collection', async () => {
-            const users = await db.collection(COLLECTION_USERS);
+    describe('insertDoc', () => {
+        it('should be defined', () => {
+            expect(crud).to.have.property('insertDoc');
+            expect(crud.insertDoc).to.be.a('function');
+        });
+
+        it('should insert a document into the collection', async () => {
+            const users = await db.collection(collection);
 
             const testUser = {
                 name: 'Jack Doe',
@@ -50,7 +62,7 @@ describe('API - Users module', () => {
 
             expect(rowsBefore).to.be.an('array').of.length(0);
 
-            const newUserResult = await insertUser(db, testUser);
+            const newUserResult = await crud.insertDoc(db, testUser);
 
             const rowsAfter = await users.find(testUser)
                 .toArray();
@@ -81,7 +93,7 @@ describe('API - Users module', () => {
                 let errorThrown = false;
 
                 try {
-                    await insertUser(db, user);
+                    await crud.insertDoc(db, user);
                 } catch (err) {
                     expect(err).to.have.property('statusCode', 400);
 
@@ -95,15 +107,20 @@ describe('API - Users module', () => {
         });
     });
 
-    describe('modifyUser', () => {
-        it('should modify a specific user', async () => {
-            const users = await db.collection(COLLECTION_USERS);
+    describe('modifyDoc', () => {
+        it('should be defined', () => {
+            expect(crud).to.have.property('modifyDoc');
+            expect(crud.modifyDoc).to.be.a('function');
+        });
+
+        it('should modify a specific document', async () => {
+            const users = await db.collection(collection);
 
             const user = await users.findOne({ name: 'John Doe' });
 
             const id = String(user._id);
 
-            await modifyUser(db, id, {
+            await crud.modifyDoc(db, id, {
                 name: 'Jack Doe'
             });
 
@@ -120,7 +137,7 @@ describe('API - Users module', () => {
         badUsers.forEach((user, index) => {
             it(`should throw an error if information is invalid (${index + 1}/${badUsers.length})`, async () => {
 
-                const users = await db.collection(COLLECTION_USERS);
+                const users = await db.collection(collection);
 
                 const userResult = await users.findOne({ name: 'John Doe' });
 
@@ -129,7 +146,7 @@ describe('API - Users module', () => {
                 let errorThrown = false;
 
                 try {
-                    await modifyUser(db, id, user);
+                    await crud.modifyDoc(db, id, user);
                 } catch (err) {
                     errorThrown = true;
 
@@ -143,9 +160,14 @@ describe('API - Users module', () => {
         });
     });
 
-    describe('deleteUser', () => {
-        it('should delete a user', async () => {
-            const users = await db.collection(COLLECTION_USERS);
+    describe('deleteDoc', () => {
+        it('should be defined', () => {
+            expect(crud).to.have.property('deleteDoc');
+            expect(crud.deleteDoc).to.be.a('function');
+        });
+
+        it('should delete a document', async () => {
+            const users = await db.collection(collection);
 
             const existingUser = await users.findOne({ name: 'John Doe' });
 
@@ -153,7 +175,7 @@ describe('API - Users module', () => {
 
             const id = String(existingUser._id);
 
-            await deleteUser(db, id);
+            await crud.deleteDoc(db, id);
 
             const newQueryResult = await users.findOne({ name: 'John Doe' });
 
@@ -161,9 +183,14 @@ describe('API - Users module', () => {
         });
     });
 
-    describe('getUsers', () => {
-        it('should get a complete list of users (employees) from the database', async () => {
-            const result = await getUsers(db);
+    describe('getDocs', () => {
+        it('should be defined', () => {
+            expect(crud).to.have.property('getDocs');
+            expect(crud.getDocs).to.be.a('function');
+        });
+
+        it('should get a complete list of documents', async () => {
+            const result = await crud.getDocs(db);
 
             expect(result).to.be.an('array').of.length(2);
 
@@ -186,3 +213,4 @@ describe('API - Users module', () => {
     });
 });
 
+