From 8e6b004a98045343b35236b450287949bd15ab52 Mon Sep 17 00:00:00 2001 From: Yousef Ahmed Date: Fri, 5 Nov 2021 03:24:10 -0500 Subject: [PATCH] Add alias support --- api/src/api/members.js | 28 ++++++++++++++++++++++ api/src/api/notes.js | 2 ++ api/src/middleware/notes.js | 43 ++++++++++++++++++++++++++++++++++ api/src/models/notes.js | 8 ++++--- client/src/pages/Note.jsx | 15 +++++++++++- client/src/utils/apiWrapper.js | 15 ++++++++++++ 6 files changed, 107 insertions(+), 4 deletions(-) diff --git a/api/src/api/members.js b/api/src/api/members.js index 4721fe1..a3d0ee5 100644 --- a/api/src/api/members.js +++ b/api/src/api/members.js @@ -13,6 +13,22 @@ const { errorMessages, } = require('../utils/members'); +const MEMBER_ALIASES = { + 'Co-Directors': ['CO_DIRECTOR'], + Directors: [ + 'CO_DIRECTOR', + 'COMMUNITY_DIRECTOR', + 'EXTERNAL_DIRECTOR', + 'TECH_DIRECTOR', + ], + Leads: [ + 'ACADEMY_LEAD', + 'TECH_LEAD', + 'PRODUCT_RESEARCH_LEAD', + 'PRODUCT_MANAGER', + ], +}; + const validateMemberQuery = (req, res, next) => { // Middleware that verifies that fields to be inserted/updated actually // exist in the Member schema, and that user has permission to update them @@ -69,6 +85,17 @@ router.get( }), ); +router.get( + '/aliases', + requireRegistered, + errorWrap(async (req, res) => { + res.json({ + success: true, + result: Object.keys(MEMBER_ALIASES), + }); + }), +); + // Create a new member // Requires Director Level router.post( @@ -218,3 +245,4 @@ router.put( ); module.exports = router; +module.exports.MEMBER_ALIASES = MEMBER_ALIASES; diff --git a/api/src/api/notes.js b/api/src/api/notes.js index d4de7f3..d3c59e8 100644 --- a/api/src/api/notes.js +++ b/api/src/api/notes.js @@ -11,6 +11,7 @@ const { const { validateEditability, validateReqParams, + parseNoteAliases, } = require('../middleware/notes'); const { encryptNote, decryptNote } = require('../utils/apiWrapper'); @@ -167,6 +168,7 @@ router.get( router.post( '/', requireLead, + parseNoteAliases, errorWrap(async (req, res) => { const memberID = req.user._id; const currentVersion = { diff --git a/api/src/middleware/notes.js b/api/src/middleware/notes.js index f3c94f0..7532b68 100644 --- a/api/src/middleware/notes.js +++ b/api/src/middleware/notes.js @@ -1,4 +1,8 @@ +const flatten = require('lodash/flatten'); + const Note = require('../models/notes'); +const Member = require('../models/members'); +const { MEMBER_ALIASES } = require('../api/members'); const { isAdmin } = require('./auth'); // middleware to validate if user can edit @@ -38,7 +42,46 @@ const validateReqParams = (req, res, next) => { next(); }; +// helper function to convert an alias to its ids +const convertAliasToIds = async (referencedIds) => { + const cleanedIds = await Promise.all( + referencedIds.map(async (idOrAlias) => { + if (!(idOrAlias in MEMBER_ALIASES)) return idOrAlias; + + const roles = MEMBER_ALIASES[idOrAlias]; + const nestedIds = await Member.find( + { role: { $in: roles }, status: 'ACTIVE' }, + { _id: 1 }, + ); + const ids = nestedIds.map((nestedId) => String(nestedId._id)); + + return ids; + }), + ); + + const flattenedIds = flatten(cleanedIds); + return flattenedIds; +}; + +// middleware to parse the aliases into their respective IDs +const parseNoteAliases = async (req, res, next) => { + const note = req.body; + + note.metaData.referencedMembers = [ + ...new Set(await convertAliasToIds(note.metaData.referencedMembers)), + ]; + note.metaData.access.editableBy = [ + ...new Set(await convertAliasToIds(note.metaData.access.editableBy)), + ]; + note.metaData.access.viewableBy = [ + ...new Set(await convertAliasToIds(note.metaData.access.viewableBy)), + ]; + + next(); +}; + module.exports = { validateEditability, validateReqParams, + parseNoteAliases, }; diff --git a/api/src/models/notes.js b/api/src/models/notes.js index 3e1c3ab..e1600b8 100644 --- a/api/src/models/notes.js +++ b/api/src/models/notes.js @@ -9,9 +9,11 @@ const actions = Object.freeze({ }); const labelsEnum = [ - '1v1', - '2v1', - '2v2', + '1:1', + '2:1', + '2:2', + 'Midpoint Reflection', + 'Final Reflection', 'Lead Interview', 'Director Interview', 'Meeting', diff --git a/client/src/pages/Note.jsx b/client/src/pages/Note.jsx index 85c002e..0902ff1 100644 --- a/client/src/pages/Note.jsx +++ b/client/src/pages/Note.jsx @@ -31,6 +31,7 @@ import { updateNote, deleteNote, getMembers, + getMemberAliases, getNoteLabels, endUserSession, } from '../utils/apiWrapper'; @@ -191,7 +192,19 @@ const Note = ({ user }: NoteProps): Node => { text: `${m.firstName} ${m.lastName}`, value: m._id, })); - setMembers(cleanedMembers); + + // get alias types + const memberAliases = await getMemberAliases(); + + const cleanedMemberAliases = (memberAliases?.data?.result ?? []).map( + (m) => ({ + key: m, + text: m, + value: m, + }), + ); + + setMembers([...cleanedMemberAliases, ...cleanedMembers]); const resNoteLabels = await getNoteLabels(); const cleanedNoteLabels = (resNoteLabels?.data?.result ?? []).map( diff --git a/client/src/utils/apiWrapper.js b/client/src/utils/apiWrapper.js index 35e4df5..2830f6b 100644 --- a/client/src/utils/apiWrapper.js +++ b/client/src/utils/apiWrapper.js @@ -110,6 +110,21 @@ export const getMembers = () => { })); }; +// Retrieves all member aliases +export const getMemberAliases = () => { + const requestString = `${BACKEND_BASE_URL}/members/aliases`; + return axios + .get(requestString, { + headers: { + 'Content-Type': 'application/JSON', + }, + }) + .catch((error) => ({ + type: 'GET_MEMBER_ALIASES_FAIL', + error, + })); +}; + // Retrieves a member's permissions from their mongo ID export const updateMember = (member, memberID) => { const requestString = `${BACKEND_BASE_URL}/members/${memberID}`;