diff --git a/src/config/router.js b/src/config/router.js index b99968a..c692f31 100644 --- a/src/config/router.js +++ b/src/config/router.js @@ -1,5 +1,6 @@ const AuthHandlers = require('../ups/auth/handlers'); const ProfileHandlers = require('../ups/profiles/handlers'); +const NoteHandlers = require('../ups/notes/handlers'); /** * Main router for our application, include all routes here. No logic should be added in this file. @@ -25,6 +26,12 @@ function router(app) { app.get('/profiles/me/:profileNumber', ProfileHandlers.getUserProfile); app.put('/profiles/uploadImage/:profileNumber', ProfileHandlers.updateUserProfileImage); app.get('/profiles/suggest', ProfileHandlers.suggestNProfiles); + + // Notes Endpoints + app.get('/notes/me/:tagferId', NoteHandlers.getAllNotes); + app.put('/notes/me/:tagferId', NoteHandlers.createNote); + app.post('/notes/me/:tagferId', NoteHandlers.updateNote); + app.delete('/notes/me/:tagferId', NoteHandlers.deleteNote); } module.exports = router; \ No newline at end of file diff --git a/src/ups/notes/dao.js b/src/ups/notes/dao.js new file mode 100644 index 0000000..a35c861 --- /dev/null +++ b/src/ups/notes/dao.js @@ -0,0 +1,77 @@ +const admin = require('firebase-admin'); + +const errors = require('../../config/errors'); +const db = admin.database(); + +/** + * Creates a new note. NoteIDs are the chronologically ordered keys generated by firebase. + * + * @param {String} from From-TagferID represents the 'from' side of the connection + * @param {String} to To-TagferID represents the 'to' side of the connection + * @param {Object} noteObject Contains the `content` and `updatedAt` timestamp + */ +function createNote(from, to, noteObject) { + const noteRef = db.ref(`notes/${from}/${to}`).push(); + return noteRef.set(noteObject) + .then(() => noteRef.key) + .catch(() => { throw errors.APP_FIREBASE_DATABASE_ERROR; }); +} + +/** + * Updates an existing note. The reason we delete and create a new entry is this allows us to levearge the keys to + * provide ordering without needing to index our data. + * + * @param {String} from From-TagferID represents the 'from' side of the connection + * @param {String} to To-TagferID represents the 'to' side of the connection + * @param {Object} noteObject Contains the `content` and `updatedAt` timestamp + */ +function updateNote(from, to, noteObject){ + const { noteId, content, updatedAt } = noteObject; + const notesRef = db.ref(`notes/${from}/${to}`); + const newNoteId = notesRef.push().key; + const updates = {}; + updates[noteId] = null; + updates[newNoteId] = { content, updatedAt }; + + return notesRef.update(updates) + .then(() => newNoteId) + .catch(() => { throw errors.APP_FIREBASE_DATABASE_ERROR; }); +} + +/** + * Delete an existing note. + * + * @param {String} from From-TagferID represents the 'from' side of the connection + * @param {String} to To-TagferID represents the 'to' side of the connection + * @param {String} noteId UUID generated by firebase + */ +function deleteNote(from, to, noteId) { + return db.ref(`notes/${from}/${to}/${noteId}`).remove().catch(() => { throw errors.APP_FIREBASE_DATABASE_ERROR; }); +} + +/** + * Gets all notes between to users. Since on creation we used chronologically ordered keys, ordering here needs to be + * in reverse. + * + * @param {String} from From-TagferID represents the 'from' side of the connection + * @param {String} to To-TagferID represents the 'to' side of the connection + */ +function getAllNotes(from, to) { + return db.ref(`notes/${from}/${to}`).once('value').then(data => { + const allNotes = new Array(data.numChildren()); + let index = data.numChildren() - 1; + data.forEach(note => { + const { content, updatedAt } = note.val(); + allNotes[index--] = { noteId: note.key, content , updatedAt }; + }); + + return allNotes; + }).catch(() => { throw errors.APP_FIREBASE_DATABASE_ERROR; }); +} + +module.exports = { + createNote, + updateNote, + deleteNote, + getAllNotes +}; diff --git a/src/ups/notes/handlers.js b/src/ups/notes/handlers.js new file mode 100644 index 0000000..3ee868b --- /dev/null +++ b/src/ups/notes/handlers.js @@ -0,0 +1,90 @@ +const authDao = require('../auth/dao'); +const notesDao = require('./dao'); + +const utils = require('../utils/utils'); + +/** + * Creates a new note + * + * @param req { content: String } + * @param res { noteId: String } | { error: AUTH_INVALID_SESSION | FIREBASE_DATABASE_ERROR } + */ +async function createNote(req,res) { + const noteObject = req.body; + const toTagferId = req.params.tagferId; + const sessionId = utils.getSessionIdFromAuthHeader(req, res); + + try { + const fromTagferId = authDao.getSession(sessionId).tagferId; + const noteId = await notesDao.createNote(fromTagferId, toTagferId, noteObject); + res.json({ noteId }); + } catch (error) { + res.json({ error }); + } +} + +/** + * Updates an existing note + * + * @param req { noteId: String, content: String } + * @param res { noteId: String } | { error: AUTH_INVALID_SESSION | FIREBASE_DATABASE_ERROR } + */ +async function updateNote(req,res) { + const noteObject = req.body; + const toTagferId = req.params.tagferId; + const sessionId = utils.getSessionIdFromAuthHeader(req, res); + + try { + const fromTagferId = authDao.getSession(sessionId).tagferId; + const noteId = await notesDao.updateNote(fromTagferId, toTagferId, noteObject); + res.json({ noteId }); + } catch (error) { + res.json({ error }); + } +} + +/** + * Deletes an existing note. + * + * @param req { noteId: String } + * @param res {} | { error: AUTH_INVALID_SESSION | FIREBASE_DATABASE_ERROR } + */ +async function deleteNote(req,res) { + const { noteId } = req.body; + const toTagferId = req.params.tagferId; + const sessionId = utils.getSessionIdFromAuthHeader(req, res); + + try { + const fromTagferId = authDao.getSession(sessionId).tagferId; + await notesDao.deleteNote(fromTagferId, toTagferId, noteId); + res.json({}); + } catch (error) { + res.json({ error }); + } +} + +/** + * Gets all notes between two users. + * + * @param req {} + * @param res [{ noteId: String, updatedAt: Number, content: String }] | { error: AUTH_INVALID_SESSION | FIREBASE_DATABASE_ERROR } + */ +async function getAllNotes(req,res) { + const toTagferId = req.params.tagferId; + const sessionId = utils.getSessionIdFromAuthHeader(req, res); + + try { + const fromTagferId = authDao.getSession(sessionId).tagferId; + const allNotes = await notesDao.getAllNotes(fromTagferId, toTagferId); + res.json({ notes: allNotes }); + } catch (error) { + res.json({ error }); + } +} + +module.exports = { + createNote, + updateNote, + deleteNote, + getAllNotes +};