From 4c4f899c6ad5c08c6f7ce80b84608ee7d183a593 Mon Sep 17 00:00:00 2001 From: John Lyon-Smith Date: Mon, 26 Mar 2018 12:41:53 -0700 Subject: [PATCH] Adde schema and route information for all entities. --- server/src/api/index.js | 3 + server/src/api/routes/ActivityRoutes.js | 153 ++++++++ server/src/api/routes/ProjectRoutes.js | 493 ------------------------ server/src/api/routes/TeamRoutes.js | 153 ++++++++ server/src/api/routes/WorkItemRoutes.js | 153 ++++++++ server/src/api/routes/index.js | 3 + server/src/database/DB.js | 2 + server/src/database/schemas/activity.js | 25 ++ server/src/database/schemas/index.js | 1 + server/src/database/schemas/team.js | 7 +- server/src/database/schemas/user.js | 5 +- server/src/database/schemas/workItem.js | 13 +- 12 files changed, 510 insertions(+), 501 deletions(-) create mode 100644 server/src/api/routes/ActivityRoutes.js delete mode 100644 server/src/api/routes/ProjectRoutes.js create mode 100644 server/src/api/routes/TeamRoutes.js create mode 100644 server/src/api/routes/WorkItemRoutes.js create mode 100644 server/src/database/schemas/activity.js diff --git a/server/src/api/index.js b/server/src/api/index.js index 9821bff..983394c 100644 --- a/server/src/api/index.js +++ b/server/src/api/index.js @@ -72,6 +72,9 @@ Promise.all([ container.authRoutes = new Routes.AuthRoutes(container) container.userRoutes = new Routes.UserRoutes(container) container.assetRoutes = new Routes.AssetRoutes(container) + container.workItemRoutes = new Routes.WorkItemRoutes(container) + container.teamRoutes = new Routes.TeamRoutes(container) + container.activityRoutes = new Routes.ActivityRoutes(container) app.use(function(req, res, next) { res.status(404).json({ diff --git a/server/src/api/routes/ActivityRoutes.js b/server/src/api/routes/ActivityRoutes.js new file mode 100644 index 0000000..de4207b --- /dev/null +++ b/server/src/api/routes/ActivityRoutes.js @@ -0,0 +1,153 @@ +import passport from 'passport' +import createError from 'http-errors' +import autobind from 'autobind-decorator' + +@autobind +export class ActivityRoutes { + constructor(container) { + const app = container.app + + this.log = container.log + this.db = container.db + this.mq = container.mq + this.ws = container.ws + + app.route('/activities') + .get(passport.authenticate('bearer', { session: false }), this.listActivitys) + .post(passport.authenticate('bearer', { session: false }), this.createActivity) + .put(passport.authenticate('bearer', { session: false }), this.updateActivity) + + app.route('/activities/:_id([a-f0-9]{24})') + .get(passport.authenticate('bearer', { session: false }), this.getActivity) + .delete(passport.authenticate('bearer', { session: false }), this.deleteActivity) + } + + listActivitys(req, res, next) { + const Activity = this.db.Activity + let limit = req.params.limit || 20 + let skip = req.params.skip || 0 + let partial = !!req.params.partial + let branch = req.params.branch + let query = {} + + if (branch) { + query.branch = branch + } + + Activity.count({}).then((total) => { + let activities = [] + let cursor = Activity.find(query).limit(limit).skip(skip).cursor().map((doc) => { + return doc.toClient(partial) + }) + + cursor.on('data', (doc) => { + activities.push(doc) + }) + cursor.on('end', () => { + res.json({ + total: total, + offset: skip, + count: activities.length, + items: activities + }) + }) + cursor.on('error', (err) => { + next(createError.InternalServerError(err.message)) + }) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + createActivity(req, res, next) { + const role = req.user.role + + // If user's role is not Executive or Administrator, return an error + if (role !== 'executive' && role !== 'administrator') { + return next(new createError.Forbidden()) + } + + // Create a new Activity template then assign it to a value in the req.body + const Activity = this.db.Activity + let activity = new Activity(req.body) + + // Save the activity (with promise) - If it doesnt, catch and throw error + activity.save().then((newActivity) => { + res.json(newActivity.toClient()) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + updateActivity(req, res, next) { + const role = req.user.role + + // If user's role is not Executive or Administrator, return an error + if (role !== 'executive' && role !== 'administrator') { + return new createError.Forbidden() + } + + // Do this here because Mongoose will add it automatically otherwise + if (!req.body._id) { + return next(createError.BadRequest('No _id given in body')) + } + + let Activity = this.db.Activity + let activityUpdates = null + + try { + activityUpdates = new Activity(req.body) + } catch (err) { + return next(createError.BadRequest('Invalid data')) + } + + Activity.findById(activityUpdates._id).then((foundActivity) => { + if (!foundActivity) { + return next(createError.NotFound(`Activity with _id ${_id} was not found`)) + } + foundActivity.merge(activityUpdates) + return foundActivity.save() + }).then((savedActivity) => { + res.json(savedActivity.toClient()) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + getActivity(req, res, next) { + const Activity = this.db.Activity + const _id = req.params._id + + Activity.findById(_id).then((activity) => { + if (!activity) { + return next(createError.NotFound(`Activity with _id ${_id} not found`)) + } + + res.json(activity.toClient()) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + deleteActivity(req, res, next) { + const role = req.user.role + + // If user's role is not Executive or Administrator, return an error + if (role !== 'executive' && role !== 'administrator') { + return new createError.Forbidden() + } + + const Activity = this.db.Activity + const _id = req.params._id + + Activity.remove({ _id }).then((activity) => { + if (!activity) { + return next(createError.NotFound(`Activity with _id ${_id} not found`)) + } + + res.json({}) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } +} diff --git a/server/src/api/routes/ProjectRoutes.js b/server/src/api/routes/ProjectRoutes.js deleted file mode 100644 index b5cf6ef..0000000 --- a/server/src/api/routes/ProjectRoutes.js +++ /dev/null @@ -1,493 +0,0 @@ -import passport from 'passport' -import createError from 'http-errors' -import { makeFingerprint } from '../makeFingerprint' -import autobind from 'autobind-decorator' - -@autobind -export class ProjectRoutes { - constructor(container) { - const app = container.app - - this.log = container.log - this.db = container.db - this.mq = container.mq - this.ws = container.ws - - app.route('/projects') - .get(passport.authenticate('bearer', { session: false }), this.listProjects) - .post(passport.authenticate('bearer', { session: false }), this.createProject) - .put(passport.authenticate('bearer', { session: false }), this.updateProject) - - app.route('/projects/:_id([a-f0-9]{24})/broker/:brokerId([a-f0-9]{24})') - .get(passport.authenticate('bearer', { session: false }), this.getProjectBrokerClientData) - - app.route('/projects/:_id([a-f0-9]{24})/broker/:brokerId([a-f0-9]{24})/sign-off') - .post(passport.authenticate('bearer', { session: false }), this.signOffProjectBrokerClientData) - - app.route('/projects/:projectId([a-f0-9]{24})/create-packages') - .post(passport.authenticate('bearer', { session: false }), this.createProjectPackages) - - app.route('/projects/:projectId([a-f0-9]{24})/reset-packages') - .post(passport.authenticate('bearer', { session: false }), this.resetProjectPackages) - - app.route('/projects/:projectId([a-f0-9]{24})/build-pdfs') - .post(passport.authenticate('bearer', { session: false }), this.buildProjectPDFs) - - app.route('/projects/:_id([a-f0-9]{24})') - .get(passport.authenticate('bearer', { session: false }), this.getProject) - .delete(passport.authenticate('bearer', { session: false }), this.deleteProject) - - app.route('/projects/import-client-data') - .post(passport.authenticate('bearer', { session: false }), this.importProjectClientData) - - app.route('/projects/:_id([a-f0-9]{24})/populated') - .get(passport.authenticate('bearer', { session: false }), this.getPopulatedProject) - - app.route('/projects/dashboard') - .get(passport.authenticate('bearer', { session: false }), this.listDashboardProjects) - - app.route('/projects/broker/:_id([a-f0-9]{24})') - .get(passport.authenticate('bearer', { session: false }), this.listBrokerProjects) - } - - listProjects(req, res, next) { - const Project = this.db.Project - let limit = req.params.limit || 20 - let skip = req.params.skip || 0 - let partial = !!req.params.partial - let branch = req.params.branch - let query = {} - - if (branch) { - query.branch = branch - } - - Project.count({}).then((total) => { - let projects = [] - let cursor = Project.find(query).limit(limit).skip(skip).cursor().map((doc) => { - return doc.toClient(partial) - }) - - cursor.on('data', (doc) => { - projects.push(doc) - }) - cursor.on('end', () => { - res.json({ - total: total, - offset: skip, - count: projects.length, - items: projects - }) - }) - cursor.on('error', (err) => { - next(createError.InternalServerError(err.message)) - }) - }).catch((err) => { - next(createError.InternalServerError(err.message)) - }) - } - - listDashboardProjects(req, res, next) { - const Project = this.db.Project - - let projects = [] - let cursor = Project.find({}).select('_id name fingerprint branch').populate({ - path: 'branch', select: '_id name fingerprint corporation', populate: { - path: 'corporation', select: '_id name imageId fingerprint' - } - }).cursor() - - cursor.on('data', (project) => { - projects.push(project) - }) - cursor.on('end', () => { - res.json({ - total: projects.length, - offset: 0, - count: projects.length, - items: projects - }) - }) - cursor.on('error', (err) => { - next(createError.InternalServerError(err.message)) - }) - } - - listBrokerProjects(req, res, next) { - const brokerId = req.params._id - const Project = this.db.Project - let projects = [] - let cursor = Project.find({ brokers: brokerId }).select('_id name clientData').cursor() - - cursor.on('data', (project) => { - projects.push(project) - }) - cursor.on('end', () => { - res.json({ - total: projects.length, - offset: 0, - count: projects.length, - items: projects - }) - }) - cursor.on('error', (err) => { - next(createError.InternalServerError(err.message)) - }) - } - - createProject(req, res, next) { - const role = req.user.role - - // If user's role is not Executive or Administrator, return an error - if (role !== 'executive' && role !== 'administrator') { - return next(new createError.Forbidden()) - } - - // Create a new Project template then assign it to a value in the req.body - const Project = this.db.Project - let project = new Project(req.body) - - project.fingerprint = makeFingerprint(project.name) - - // Save the project (with promise) - If it doesnt, catch and throw error - project.save().then((newProject) => { - res.json(newProject.toClient()) - }).catch((err) => { - next(createError.InternalServerError(err.message)) - }) - } - - updateProject(req, res, next) { - const role = req.user.role - - // If user's role is not Executive or Administrator, return an error - if (role !== 'executive' && role !== 'administrator') { - return new createError.Forbidden() - } - - // Do this here because Mongoose will add it automatically otherwise - if (!req.body._id) { - return next(createError.BadRequest('No _id given in body')) - } - - let Project = this.db.Project - let projectUpdates = null - - try { - projectUpdates = new Project(req.body) - } catch (err) { - return next(createError.BadRequest('Invalid data')) - } - - if (projectUpdates.name) { - projectUpdates.fingerprint = makeFingerprint(projectUpdates.name) - } - - Project.findById(projectUpdates._id).then((foundProject) => { - if (!foundProject) { - return next(createError.NotFound(`Project with _id ${_id} was not found`)) - } - foundProject.merge(projectUpdates) - return foundProject.save() - }).then((savedProject) => { - res.json(savedProject.toClient()) - }).catch((err) => { - next(createError.InternalServerError(err.message)) - }) - } - - importProjectClientData(req, res, next) { - const role = req.user.role - - if (role !== 'broker' && role !== 'executive' && role !== 'administrator') { - return next(new createError.Forbidden()) - } - - const { _id, brokerId, assetId } = req.body - - if (!_id || !brokerId || !assetId) { - return next(createError.BadRequest('Must specify _id, brokerId and assetId')) - } - - this.mq.request('dar-import', 'importClientData', { - projectId: _id, - brokerId: brokerId, - assetId: assetId, - passback: { - rooms: [ req.user._id ], - projectId: _id, - brokerId: brokerId, - routerName: 'projectRoutes', - funcName: 'completeImportClientData' - } - }).then((correlationId) => { - res.json({ correlationId }) - }).catch((err) => { - // TODO: Should delete the asset - next(createError.InternalServerError('Unable to import uploaded file to project')) - }) - } - - completeImportClientData(passback, error, data) { - if (!passback.rooms) { - return - } - - let obj = { - brokerId: passback.brokerId, - projectId: passback.projectId, - } - - if (error) { - obj.problems = error.problems - obj.error = error.message - } - - this.ws.notify(passback.rooms, 'clientDataImportComplete', obj) - } - - getProject(req, res, next) { - const Project = this.db.Project - const _id = req.params._id - - Project.findById(_id).then((project) => { - if (!project) { - return next(createError.NotFound(`Project with _id ${_id} not found`)) - } - - res.json(project.toClient()) - }).catch((err) => { - next(createError.InternalServerError(err.message)) - }) - } - - getPopulatedProject(req, res, next) { - const Project = this.db.Project - const _id = req.params._id - - Project.findById(_id).then((project) => { - if (!project) { - return next(createError.NotFound(`Project with _id ${_id} not found`)) - } - - return project.populate({ path: 'brokers', select: '_id firstName lastName email thumbnailImageId t12 aum numHouseholds homePhone cellPhone' }).execPopulate() - }).then((project) => { - res.json(project.toClient()) - }).catch((err) => { - next(createError.InternalServerError(err.message)) - }) - } - - getProjectBrokerClientData(req, res, next) { - const Project = this.db.Project - const _id = req.params._id - const brokerId = req.params.brokerId - - Project.findById(_id).select('clientData').then((project) => { - if (!project) { - return next(createError.NotFound(`Project with _id ${_id} not found`)) - } - - if (!project.clientData) { - return res.json({}) - } - - const clientData = project.clientData.find(data => data.brokerId.toString() === brokerId) - - if (clientData) { - res.json(clientData) - } else { - res.json({}) - } - }).catch((error) => { - next(createError.InternalServerError(err.message)) - }) - } - - signOffProjectBrokerClientData(req, res, next) { - const Project = this.db.Project - const _id = req.params._id - const brokerId = req.params.brokerId - - Project.findById(_id).then((project) => { - if (!project) { - return next(createError.NotFound(`Project with _id ${_id} not found`)) - } - - for (let clientData of project.clientData) { - if (clientData.brokerId && clientData.brokerId.toString() === brokerId) { - if (clientData.submitted || clientData.numProblems !== 0 || clientData.problems.length !== 0 || clientData.error) { - return next(createError.BadRequest(`Project ${_id}, broker ${brokerId} cannot be signed off at this time`)) - } - - clientData.submitted = new Date() - return project.save(project) - } - } - - return next(createError.NotFound(`Client data for broker ${brokerId} was not found`)) - }).then((savedProject) => { - res.json({}) - }).catch((error) => { - next(createError.InternalServerError(error.message)) - }) - } - - createProjectPackages(req, res, next) { - const role = req.user.role - - if (role !== 'executive' && role !== 'administrator') { - return next(new createError.Forbidden()) - } - - const { projectId } = req.params - - this.mq.request('dar-import', 'createPackages', { - projectId, - passback: { - rooms: [ req.user._id ], // TODO: Add a room for the specific project - projectId, - routerName: 'projectRoutes', - funcName: 'completeCreatePackageData' - } - }).then((correlationId) => { - res.json({ correlationId }) - }).catch((err) => { - next(createError.InternalServerError('Unable to create package data')) - }) - } - - resetProjectPackages(req, res, next) { - const role = req.user.role - - if (role !== 'executive' && role !== 'administrator') { - return next(new createError.Forbidden()) - } - - const { projectId } = req.params - const Package = this.db.Package - const cursor = Package.find({ project: projectId }).cursor() - let totalPDFs = 0 - let totalPackages = 0 - - new Promise((resolve, reject) => { - cursor.on('data', (pkg) => { - pkg.remove().then((pkg) => { - if (pkg.assetId) { - totalPDFs += 1 - return this.db.gridfs.remove({ _id: pkg.assetId }) - } - }).catch((error) => { - reject(error) - }) - - totalPackages += 1 - }) - cursor.on('end', () => { - resolve() - }) - cursor.on('error', (error) => { - reject(error) - }) - }).then(() => { - res.json({ totalPackages, totalPDFs }) - }).catch((error) => { - next(createError.InternalServerError('Unable to delete all project packages')) - }) - } - - completeCreatePackageData(passback, error, data) { - if (!passback.rooms) { - return - } - - let obj = { - projectId: passback.projectId, - } - - if (error) { - obj.problems = error.problems - obj.error = error.message - } - - this.ws.notify(passback.rooms, 'packageGenerationComplete', obj) - } - - buildProjectPDFs(req, res, next) { - const role = req.user.role - - if (role !== 'executive' && role !== 'administrator') { - return next(new createError.Forbidden()) - } - - const projectId = req.params.projectId - const Package = this.db.Package - let total = 0 - let cursor = Package.find({ project: projectId }).cursor() - - cursor.on('data', (pkg) => { - const packageId = pkg._id - this.mq.request('dar-pdf', 'createPackagePDF', { - packageId, - passback: { - rooms: [ req.user._id ], // TODO: Add a room for the specific project - packageId, - routerName: 'projectRoutes', - funcName: 'completeCreatePackagePDF' - } - }).then((correlationId) => { - res.json({ correlationId }) - }).catch((err) => { - // TODO: Collect errors and return to user - }) - total += 1 - }) - cursor.on('end', () => { - res.json({ total }) - }) - cursor.on('error', (err) => { - next(createError.InternalServerError(err.message)) - }) - } - - completeCreatePackagePDF(passback, error, data) { - if (!passback.rooms) { - return - } - - let obj = { - packageId: passback.packageId, - } - - if (error) { - obj.problems = error.problems - obj.error = error.message - } else { - obj.assetId = data.assetId - } - - this.ws.notify(passback.rooms, 'createPackagePDFComplete', obj) - } - - deleteProject(req, res, next) { - const role = req.user.role - - // If user's role is not Executive or Administrator, return an error - if (role !== 'executive' && role !== 'administrator') { - return new createError.Forbidden() - } - - const Project = this.db.Project - const _id = req.params._id - - Project.remove({ _id }).then((project) => { - if (!project) { - return next(createError.NotFound(`Project with _id ${_id} not found`)) - } - - res.json({}) - }).catch((err) => { - next(createError.InternalServerError(err.message)) - }) - } -} diff --git a/server/src/api/routes/TeamRoutes.js b/server/src/api/routes/TeamRoutes.js new file mode 100644 index 0000000..2051403 --- /dev/null +++ b/server/src/api/routes/TeamRoutes.js @@ -0,0 +1,153 @@ +import passport from 'passport' +import createError from 'http-errors' +import autobind from 'autobind-decorator' + +@autobind +export class TeamRoutes { + constructor(container) { + const app = container.app + + this.log = container.log + this.db = container.db + this.mq = container.mq + this.ws = container.ws + + app.route('/teams') + .get(passport.authenticate('bearer', { session: false }), this.listTeams) + .post(passport.authenticate('bearer', { session: false }), this.createTeam) + .put(passport.authenticate('bearer', { session: false }), this.updateTeam) + + app.route('/teams/:_id([a-f0-9]{24})') + .get(passport.authenticate('bearer', { session: false }), this.getTeam) + .delete(passport.authenticate('bearer', { session: false }), this.deleteTeam) + } + + listTeams(req, res, next) { + const Team = this.db.Team + let limit = req.params.limit || 20 + let skip = req.params.skip || 0 + let partial = !!req.params.partial + let branch = req.params.branch + let query = {} + + if (branch) { + query.branch = branch + } + + Team.count({}).then((total) => { + let teams = [] + let cursor = Team.find(query).limit(limit).skip(skip).cursor().map((doc) => { + return doc.toClient(partial) + }) + + cursor.on('data', (doc) => { + teams.push(doc) + }) + cursor.on('end', () => { + res.json({ + total: total, + offset: skip, + count: teams.length, + items: teams + }) + }) + cursor.on('error', (err) => { + next(createError.InternalServerError(err.message)) + }) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + createTeam(req, res, next) { + const role = req.user.role + + // If user's role is not Executive or Administrator, return an error + if (role !== 'executive' && role !== 'administrator') { + return next(new createError.Forbidden()) + } + + // Create a new Team template then assign it to a value in the req.body + const Team = this.db.Team + let team = new Team(req.body) + + // Save the team (with promise) - If it doesnt, catch and throw error + team.save().then((newTeam) => { + res.json(newTeam.toClient()) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + updateTeam(req, res, next) { + const role = req.user.role + + // If user's role is not Executive or Administrator, return an error + if (role !== 'executive' && role !== 'administrator') { + return new createError.Forbidden() + } + + // Do this here because Mongoose will add it automatically otherwise + if (!req.body._id) { + return next(createError.BadRequest('No _id given in body')) + } + + let Team = this.db.Team + let teamUpdates = null + + try { + teamUpdates = new Team(req.body) + } catch (err) { + return next(createError.BadRequest('Invalid data')) + } + + Team.findById(teamUpdates._id).then((foundTeam) => { + if (!foundTeam) { + return next(createError.NotFound(`Team with _id ${_id} was not found`)) + } + foundTeam.merge(teamUpdates) + return foundTeam.save() + }).then((savedTeam) => { + res.json(savedTeam.toClient()) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + getTeam(req, res, next) { + const Team = this.db.Team + const _id = req.params._id + + Team.findById(_id).then((team) => { + if (!team) { + return next(createError.NotFound(`Team with _id ${_id} not found`)) + } + + res.json(team.toClient()) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + deleteTeam(req, res, next) { + const role = req.user.role + + // If user's role is not Executive or Administrator, return an error + if (role !== 'executive' && role !== 'administrator') { + return new createError.Forbidden() + } + + const Team = this.db.Team + const _id = req.params._id + + Team.remove({ _id }).then((team) => { + if (!project) { + return next(createError.NotFound(`Team with _id ${_id} not found`)) + } + + res.json({}) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } +} diff --git a/server/src/api/routes/WorkItemRoutes.js b/server/src/api/routes/WorkItemRoutes.js new file mode 100644 index 0000000..0769d5b --- /dev/null +++ b/server/src/api/routes/WorkItemRoutes.js @@ -0,0 +1,153 @@ +import passport from 'passport' +import createError from 'http-errors' +import autobind from 'autobind-decorator' + +@autobind +export class WorkItemRoutes { + constructor(container) { + const app = container.app + + this.log = container.log + this.db = container.db + this.mq = container.mq + this.ws = container.ws + + app.route('/workitems') + .get(passport.authenticate('bearer', { session: false }), this.listWorkItems) + .post(passport.authenticate('bearer', { session: false }), this.createWorkItem) + .put(passport.authenticate('bearer', { session: false }), this.updateWorkItem) + + app.route('/workitems/:_id([a-f0-9]{24})') + .get(passport.authenticate('bearer', { session: false }), this.getWorkItem) + .delete(passport.authenticate('bearer', { session: false }), this.deleteWorkItem) + } + + listWorkItems(req, res, next) { + const WorkItem = this.db.WorkItem + let limit = req.params.limit || 20 + let skip = req.params.skip || 0 + let partial = !!req.params.partial + let branch = req.params.branch + let query = {} + + if (branch) { + query.branch = branch + } + + WorkItem.count({}).then((total) => { + let workItems = [] + let cursor = WorkItem.find(query).limit(limit).skip(skip).cursor().map((doc) => { + return doc.toClient(partial) + }) + + cursor.on('data', (doc) => { + workItems.push(doc) + }) + cursor.on('end', () => { + res.json({ + total: total, + offset: skip, + count: workItems.length, + items: workItems + }) + }) + cursor.on('error', (err) => { + next(createError.InternalServerError(err.message)) + }) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + createWorkItem(req, res, next) { + const role = req.user.role + + // If user's role is not Executive or Administrator, return an error + if (role !== 'executive' && role !== 'administrator') { + return next(new createError.Forbidden()) + } + + // Create a new WorkItem template then assign it to a value in the req.body + const WorkItem = this.db.WorkItem + let workItem = new WorkItem(req.body) + + // Save the workItem (with promise) - If it doesnt, catch and throw error + workItem.save().then((newWorkItem) => { + res.json(newWorkItem.toClient()) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + updateWorkItem(req, res, next) { + const role = req.user.role + + // If user's role is not Executive or Administrator, return an error + if (role !== 'executive' && role !== 'administrator') { + return new createError.Forbidden() + } + + // Do this here because Mongoose will add it automatically otherwise + if (!req.body._id) { + return next(createError.BadRequest('No _id given in body')) + } + + let WorkItem = this.db.WorkItem + let workItemUpdates = null + + try { + workItemUpdates = new WorkItem(req.body) + } catch (err) { + return next(createError.BadRequest('Invalid data')) + } + + WorkItem.findById(workItemUpdates._id).then((foundWorkItem) => { + if (!foundWorkItem) { + return next(createError.NotFound(`WorkItem with _id ${_id} was not found`)) + } + foundWorkItem.merge(workItemUpdates) + return foundWorkItem.save() + }).then((savedWorkItem) => { + res.json(savedWorkItem.toClient()) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + getWorkItem(req, res, next) { + const WorkItem = this.db.WorkItem + const _id = req.params._id + + WorkItem.findById(_id).then((workItem) => { + if (!workItem) { + return next(createError.NotFound(`WorkItem with _id ${_id} not found`)) + } + + res.json(workItem.toClient()) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } + + deleteWorkItem(req, res, next) { + const role = req.user.role + + // If user's role is not Executive or Administrator, return an error + if (role !== 'executive' && role !== 'administrator') { + return new createError.Forbidden() + } + + const WorkItem = this.db.WorkItem + const _id = req.params._id + + WorkItem.remove({ _id }).then((workItem) => { + if (!workItem) { + return next(createError.NotFound(`WorkItem with _id ${_id} not found`)) + } + + res.json({}) + }).catch((err) => { + next(createError.InternalServerError(err.message)) + }) + } +} diff --git a/server/src/api/routes/index.js b/server/src/api/routes/index.js index 57aa9e1..9f20d11 100644 --- a/server/src/api/routes/index.js +++ b/server/src/api/routes/index.js @@ -1,3 +1,6 @@ export { AuthRoutes } from './AuthRoutes' export { AssetRoutes } from './AssetRoutes' export { UserRoutes } from './UserRoutes' +export { WorkItemRoutes } from './WorkItemRoutes' +export { ActivityRoutes } from './ActivityRoutes' +export { TeamRoutes } from './TeamRoutes' diff --git a/server/src/database/DB.js b/server/src/database/DB.js index 96e498b..acdc209 100644 --- a/server/src/database/DB.js +++ b/server/src/database/DB.js @@ -25,6 +25,8 @@ export class DB { this.User = connection.model('User', Schemas.userSchema) this.WorkItem = connection.model('WorkItem', Schemas.workItemSchema) + this.Activity = connection.model('Activity', Schemas.activitySchema) + this.Team = connection.model('Team', Schemas.teamSchema) return Promise.resolve(this) }) diff --git a/server/src/database/schemas/activity.js b/server/src/database/schemas/activity.js new file mode 100644 index 0000000..b85a930 --- /dev/null +++ b/server/src/database/schemas/activity.js @@ -0,0 +1,25 @@ +import { Schema } from 'mongoose' + +export let activitySchema = new Schema({ + _id: { type: Schema.Types.ObjectId, required: true, auto: true }, + resolution: String, + workItem: { type: Schema.Types.ObjectId, required: true }, + status: { type: String, required: true, enum: { + values: [ 'planned', 'open', 'onHold', 'closed'], + message: 'enum validator failed for path `{PATH}` with value `{VALUE}`' + }}, + notes: String, + when: Date, + loc: { + type: { type: String }, + coordinates: [Number], + }, + address: String, + fromStreetNumber: Number, + toStreetNumber: Number, + photos: [ Schema.Types.ObjectId ], +}, { timestamps: true, id: false }) + +activitySchema.methods.toClient = function() { + return this.toObject() +} diff --git a/server/src/database/schemas/index.js b/server/src/database/schemas/index.js index 4d102bf..3fff8d6 100644 --- a/server/src/database/schemas/index.js +++ b/server/src/database/schemas/index.js @@ -1,3 +1,4 @@ export { workItemSchema } from './workItem' export { userSchema } from './user' export { teamSchema } from './team' +export { activitySchema } from './activity' diff --git a/server/src/database/schemas/team.js b/server/src/database/schemas/team.js index a934aed..55e391e 100644 --- a/server/src/database/schemas/team.js +++ b/server/src/database/schemas/team.js @@ -1,6 +1,9 @@ import { Schema } from 'mongoose' -export let team = new Schema({ +export let teamSchema = new Schema({ name: { type: String }, - members: { type: Schema.Types.ObjectId, required: true }, }, { timestamps: true, id: false }) + +teamSchema.methods.toClient = function() { + return this.toObject() +} diff --git a/server/src/database/schemas/user.js b/server/src/database/schemas/user.js index e3c1a84..85a637a 100644 --- a/server/src/database/schemas/user.js +++ b/server/src/database/schemas/user.js @@ -15,7 +15,6 @@ export let userSchema = new Schema({ }, email: { type: String, match: regExpPattern.email, required: true, index: true, unique: true }, newEmail: { type: String, match: regExpPattern.email }, - imageId: { type: Schema.Types.ObjectId }, thumbnailImageId: { type: Schema.Types.ObjectId }, emailToken: { type: { @@ -32,6 +31,7 @@ export let userSchema = new Schema({ firstName: { type: String, required: true }, lastName: { type: String, required: true }, administrator: { type: Boolean, required: true }, + team: Schema.Types.ObjectId, }, { timestamps: true, id: false }) userSchema.methods.toClient = function(authUser) { @@ -47,7 +47,8 @@ userSchema.methods.toClient = function(authUser) { thumbnailImageId: this.thumbnailImageId, firstName: this.firstName, lastName: this.lastName, - administrator: this.administrator + administrator: this.administrator, + team: this.team } return user diff --git a/server/src/database/schemas/workItem.js b/server/src/database/schemas/workItem.js index 04a9bf9..3f7b934 100644 --- a/server/src/database/schemas/workItem.js +++ b/server/src/database/schemas/workItem.js @@ -7,11 +7,16 @@ export let workItemSchema = new Schema({ values: [ 'order', 'inspection', 'complaint'], message: 'enum validator failed for path `{PATH}` with value `{VALUE}`' }}, + number: Number, + loc: { + type: { type: String }, + coordinates: [Number], + }, + address: String, + photos: [ Schema.Types.ObjectId ], + details: String, }, { timestamps: true, id: false }) workItemSchema.methods.toClient = function() { - return { - _id: this._id, - workItemType: this.workItemType - } + return this.toObject() }