Skip to content
This repository has been archived by the owner on Aug 3, 2018. It is now read-only.

Commit

Permalink
feat: add meeting routes + helper
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian committed Dec 28, 2017
1 parent ff3ac90 commit e94a966
Show file tree
Hide file tree
Showing 15 changed files with 571 additions and 161 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"start": "nodemon src/server.js --exec babel-node",
"build": "babel src -d dist",
"lint": "eslint src",
"test": "NODE_ENV=test mocha --compilers js:babel-core/register src/**/*.spec.js",
"test": "NODE_ENV=test mocha --compilers js:babel-core/register src/**/_meeting.spec.js",
"serve": "node src/server.js"
},
"dependencies": {
Expand Down
65 changes: 29 additions & 36 deletions src/app/helper/appointment.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ import {logger} from '../helper/logger.js';
import Settings from '../models/settings.model.js';
import Appointment from '../models/appointment.model.js';
import User from '../models/user.model.js';
import Meeting from '../models/meeting.model.js';
import moment from 'moment-timezone';
import webpush from 'web-push';

/**
* Converts Date or moment object to Integer representing the time.
*
* @param {moment/Date} Date or moment object
* @return {Number} Time as Number
*/
function timeToNumber(time): Number {
return time instanceof Date
? time.getHours() * 100 + time.getMinutes()
: (time instanceof moment ? time.hours() * 100 + time.minutes() : 0);
}

let appointmentHelper = {
/**
* Converts Date or moment object to Integer representing the time.
*
* @param {moment/Date} Date or moment object
* @return {Number} Time as Number
*/
timeToNumber: time => time instanceof Date
? time.getHours() * 100 + time.getMinutes()
: (time instanceof moment ? time.hours() * 100 + time.minutes() : 0),

/**
* Parses hour as String.
*
Expand Down Expand Up @@ -56,7 +56,19 @@ let appointmentHelper = {
}

return appointment;
}
},

/**
* Checks whether an user is authorized for an appointment.
* This applies if he is the holder or an admin.
*/
// TODO: This doesn't work always as expected
isAuthorized: (user, appointment) => appointment !== null && user !== null && 'user' in appointment &&
('role' in user && user.role === 'ROLE_ADMIN' || '_id' in user &&
('_id' in appointment.user
? appointment.user._id.toString() === user._id.toString()
: appointment.user.toString() === user._id.toString()
))
};

/**
Expand Down Expand Up @@ -147,7 +159,7 @@ appointmentHelper.notify = () => new Promise(async (resolve) => {
/**
* Find an appointment for the given date/time.
*
* @param {'Datable'} datetime The date, moment, date-string or 'now'
* @param {Date/Moment} datetime The date, moment, date-string or 'now'
* @param {number} toleranceBefore The minutes of tolerance before
* @param {number} toleranceAfter The minutes tolerance after
* @return The appointment object
Expand Down Expand Up @@ -182,13 +194,13 @@ appointmentHelper.getAt = (datetime, toleranceBefore = 0, toleranceAfter = 0) =>
{
weekDay: minDate.weekday(),
hour: {
$gte: timeToNumber(minDate)
$gte: appointmentHelper.timeToNumber(minDate)
}
},
{
weekDay: maxDate.weekday(),
hour: {
$lte: timeToNumber(maxDate)
$lte: appointmentHelper.timeToNumber(maxDate)
}
}
]
Expand All @@ -200,8 +212,8 @@ appointmentHelper.getAt = (datetime, toleranceBefore = 0, toleranceAfter = 0) =>
.findOne({
weekDay: minDate.weekday(),
hour: {
$gte: timeToNumber(minDate),
$lte: timeToNumber(maxDate)
$gte: appointmentHelper.timeToNumber(minDate),
$lte: appointmentHelper.timeToNumber(maxDate)
}
})
.populate('user', 'name username gravatarHash')
Expand All @@ -211,23 +223,4 @@ appointmentHelper.getAt = (datetime, toleranceBefore = 0, toleranceAfter = 0) =>
resolve(appointmentHelper.addIncrement(doc, increment));
});

/**
* Searches for appointments the user is allowed to
* join right now. For regular users this means at
* the time of their booked appointment (with a 5 minute
* tolerance). For admins marked as teacher, it means
* within the timespan of 5 minutes before any appointment
* starts and 20 minutes after it was scheduled.
*
* @param {Object} user User that needs authorization
* @param {Boolean} reconnect If set to true, the time tolarance
* for regular users is the same as for
* admins.
*
* @return the appointment object
*/
appointmentHelper.getNowAuthorized = (user, reconnect = false) => appointmentHelper.getAt('now', 5,
reconnect || (user && user.role === 'ROLE_ADMIN' && user.appointmentsCallee === true) ? 25 : 8
);

export default appointmentHelper;
2 changes: 2 additions & 0 deletions src/app/helper/appointment.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,6 @@ describe('Appointment Helper', () => {
});
});

it('should ')

});
53 changes: 53 additions & 0 deletions src/app/helper/meeting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Meeting from '../models/meeting.model.js';
import moment from 'moment';

export default {

/**
* Gets an ongoing meeting
*/
getOngoing: () => Meeting
.findOne({
closedAt: { $exists: false },
createdAt: { $gt: moment().subtract(25, 'minutes') }
})
.populate('messages')
.populate('appointment')
.populate('participants', 'name username gravatarHash country'),

/**
* Gets an ongoing meeting for a given user
*/
getOngoingByUser: user => Meeting
.findOne({
participants: user._id,
closedAt: { $exists: false },
createdAt: { $gt: moment().subtract(25, 'minutes') }
})
.populate('messages')
.populate('appointment')
.populate('participants', 'name username gravatarHash country'),

/**
* Gets an ongoing meeting and adds a given user to it.
* In case of non-existance a new meeting is created.
*/
getOngoingOrCreate: (appointment, user) => Meeting
.findOneAndUpdate({
appointment: appointment._id,
closedAt: { $exists: false },
createdAt: { $gt: moment().subtract(25, 'minutes') }
}, {
$addToSet: {
participants: user._id
},
appointment: appointment._id
}, {
new: true,
upsert: true,
setDefaultsOnInsert: true
})
.populate('appointment')
.populate('messages')
.populate('participants', 'name username gravatarHash country')
};
13 changes: 13 additions & 0 deletions src/app/models/meeting.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import mongoose from 'mongoose';

let MeetingSchema = mongoose.Schema({
participants: [{ type : mongoose.Schema.Types.ObjectId, ref: 'User' }],
messages: [{ type : mongoose.Schema.Types.ObjectId, ref: 'Message' }],
appointment: { type : mongoose.Schema.Types.ObjectId, ref: 'Appointment' },
startedAt: Date,
closedAt: Date
}, {
timestamps: true
});

export default mongoose.model('Meeting', MeetingSchema);
3 changes: 2 additions & 1 deletion src/app/models/message.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ let messageSchema = mongoose.Schema({
text : { type: String, required: true, maxlength: 1000 },
user : { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
edited: Boolean,
deleted: Boolean
deleted: Boolean,
private: Boolean
}, {
timestamps: true
});
Expand Down
3 changes: 1 addition & 2 deletions src/app/models/user.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ let userSchema = mongoose.Schema({
testimonial: Boolean,
appointment: [{ type: mongoose.Schema.Types.ObjectId, ref: 'PushSubscriptions' }]
},
appointments: [{ type: Date }],
appointmentsCallee: Boolean
appointments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'AppointmentCall' }],
});

userSchema.methods.generateHash = function(password) {
Expand Down
2 changes: 2 additions & 0 deletions src/app/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import userRoutes from './routes/_user.router.js';
import liveRoutes from './routes/_livestream.router.js';
import settingsRoutes from './routes/_settings.router.js';
import analyticsRoutes from './routes/_analytics.router.js';
import meetingRoutes from './routes/_meeting.router.js';
import pushRoutes from './routes/_push.router.js';
import mongoose from 'mongoose';
import User from './models/user.model.js';
Expand Down Expand Up @@ -66,6 +67,7 @@ export default (app, router, passport, io) => {
settingsRoutes(app, router, admin);
analyticsRoutes(app, router, admin);
pushRoutes(app, router);
meetingRoutes(app, router, io, admin);

// Provide a simple status page
// Return 204 (No Content) when mongoose is connected
Expand Down
7 changes: 7 additions & 0 deletions src/app/routes/_appointment.router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Appointment from '../models/appointment.model.js';
import Settings from '../models/settings.model.js';
import { logger } from '../helper/logger.js';
import appointHelper from '../helper/appointment.js';
let ObjectId = require('mongoose').Types.ObjectId;

export default (app, router, io, admin) => {

Expand Down Expand Up @@ -220,6 +221,12 @@ export default (app, router, io, admin) => {
// notify possible updates
appointHelper.notify().then();

// remove reference from appointment calls
await AppointmentCall
.find({ appointment: ObjectId(req.params.id) })
.remove()
.exec();

res.json(result);
} catch (err) {
res.send(err);
Expand Down
Loading

0 comments on commit e94a966

Please sign in to comment.