forked from se701g2/Doto
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into dragtoadd
- Loading branch information
Showing
32 changed files
with
653 additions
and
199 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ const taskSchema = mongoose.Schema({ | |
}, | ||
isComplete: { | ||
type: Boolean, | ||
default: false, | ||
}, | ||
}); | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
const express = require("express"); | ||
const router = express.Router(); | ||
const authenticateToken = require("../config/token-setup").authenticateToken; | ||
const reminderService = require("../webpush/reminder-service"); | ||
|
||
router.post("/subscribe", authenticateToken, (req, res) => { | ||
reminderService.subscribe(req.user.email, req.body); | ||
res.status(201).json({}); | ||
}); | ||
|
||
module.exports = router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
const cron = require("node-cron"); | ||
const Task = require("../models/Task"); | ||
const webpush = require("web-push"); | ||
const { logger } = require("../common/logging"); | ||
|
||
webpush.setVapidDetails("mailto:[email protected]", process.env.VAPID_PUBLIC_KEY, process.env.VAPID_PRIVATE_KEY); | ||
|
||
// Maps user.email to a push manager subscription so we know which client to | ||
// send a reminder to. | ||
const subscriptions = new Map(); | ||
|
||
// Every minute query the database to check if there are tasks that should be | ||
// fired off via web push. Note this means a notification will be delivered | ||
// one minute late in the worst case. | ||
cron.schedule("* * * * *", () => { | ||
Task.find( | ||
{ reminderDate: { $lte: new Date() }, user: { $in: [...subscriptions.keys()] }, isComplete: false }, | ||
(err, tasks) => { | ||
if (err) { | ||
logger.error(err); | ||
return; | ||
} | ||
for (let task of tasks) { | ||
const subscription = subscriptions.get(task.user); | ||
if (subscription) { | ||
webpush | ||
.sendNotification(subscription, JSON.stringify(task)) | ||
.then(() => { | ||
logger.info(`Fired notification id=${task.id} title=${task.title}`); | ||
// This is a bit of a hack. | ||
// Unsetting the field means the notification is fired so we can avoid duplicating. | ||
task.reminderDate = undefined; | ||
task.save(); | ||
}) | ||
.catch((err) => { | ||
logger.error(err.stack); | ||
}); | ||
} else { | ||
logger.error("Subscription not found. This should never occur."); | ||
} | ||
} | ||
}, | ||
); | ||
}); | ||
|
||
const subscribe = (id, subscription) => { | ||
if (typeof id === "string" && subscription && subscription.endpoint) { | ||
subscriptions.set(id, subscription); | ||
logger.info(`Registered subscription for ${id}`); | ||
} | ||
}; | ||
const unsubscribe = (id) => { | ||
subscriptions.delete(id); | ||
logger.info(`Removed subscription for ${id}`); | ||
}; | ||
|
||
module.exports = { subscribe, unsubscribe }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ const UserModel = require("../src/models/User"); | |
const assert = require("assert"); | ||
|
||
const validUser = new UserModel({ | ||
email: "[email protected]", | ||
name: "john", | ||
picture: "profile.png", | ||
themePreference: "dark", | ||
|
@@ -152,6 +153,26 @@ describe("Task Model Tests", function () { | |
}); | ||
}); | ||
|
||
it('update task sucessfully', async function () { | ||
await validTask.save(); | ||
const savedTask = await TaskModel.findOne({_id: validTask._id}); | ||
|
||
await savedTask.update({ title: 'updated title' }); | ||
|
||
const updatedTask = await TaskModel.findOne({_id: validTask._id}); | ||
assert(updatedTask.title === 'updated title'); | ||
}); | ||
|
||
it("delete task successfully.", async function () { | ||
await validTask.save(); | ||
const savedTask = await TaskModel.findOne(); | ||
|
||
await savedTask.remove(); | ||
const newSavedTask = await TaskModel.findOne({_id: validTask._id}); | ||
|
||
assert(newSavedTask === null); | ||
}); | ||
|
||
it("update one isComplete status to true", async function () { | ||
TaskModel.updateOne({ taskId: validTask.taskId }, { isComplete: true }) | ||
.then(() => TaskModel.findOne({ taskId: validTask.taskId })) | ||
|
@@ -175,4 +196,110 @@ describe("Task Model Tests", function () { | |
assert(task.isComplete === true); | ||
}); | ||
}); | ||
|
||
// Begin reminder service tests | ||
// | ||
// TODO - We should clear the database after each unit test. | ||
// | ||
// Current workaround is to create new model objects and | ||
// increment the (unique) id so that tests are 'stateless' | ||
// i.e. do not depend on order of execution. | ||
it("retrieves tasks with reminderDate lte to current date", async function () { | ||
const testTask = new TaskModel({ | ||
user: "[email protected]", | ||
taskId: "2", | ||
title: "title", | ||
description: "Re-Doing all the things", | ||
location: "science building", | ||
priority: 0, | ||
duration: 120, | ||
reminderDate: "2020-07-14T07:50:00+12:00", | ||
startDate: "2020-08-14T08:50:00+12:00", | ||
endDate: "2020-08-14T07:50:00+12:00", | ||
isComplete: false, | ||
}); | ||
await testTask.save(); | ||
const [retrievedTask] = await TaskModel.find({ | ||
taskId: "2", | ||
reminderDate: { $lte: new Date(testTask.reminderDate.getTime() + 1) }, | ||
user: { $in: [testTask.user] }, | ||
isComplete: false, | ||
}).exec(); | ||
|
||
assert.equal(retrievedTask.taskID, testTask.taskID); | ||
}); | ||
|
||
it("does not retrieve tasks with unset reminder date", async function () { | ||
const testTask = new TaskModel({ | ||
user: "[email protected]", | ||
taskId: "3", | ||
title: "title", | ||
description: "Re-Doing all the things", | ||
location: "science building", | ||
priority: 0, | ||
duration: 120, | ||
startDate: "2020-08-14T08:50:00+12:00", | ||
endDate: "2020-08-14T07:50:00+12:00", | ||
isComplete: false, | ||
}); | ||
await testTask.save(); | ||
const retrievedTasks = await TaskModel.find({ | ||
taskId: "3", | ||
reminderDate: { $lte: new Date(validTask.reminderDate) }, | ||
user: { $in: [testTask.user] }, | ||
isComplete: false, | ||
}).exec(); | ||
|
||
assert(retrievedTasks.length === 0); | ||
}); | ||
|
||
it("does not retrieve tasks with future reminder date", async function () { | ||
const testTask = new TaskModel({ | ||
user: "[email protected]", | ||
taskId: "4", | ||
title: "title", | ||
description: "Re-Doing all the things", | ||
location: "science building", | ||
priority: 0, | ||
duration: 120, | ||
reminderDate: "2020-07-14T07:50:00+12:00", | ||
startDate: "2020-08-14T08:50:00+12:00", | ||
endDate: "2020-08-14T07:50:00+12:00", | ||
isComplete: false, | ||
}); | ||
await testTask.save(); | ||
const retrievedTasks = await TaskModel.find({ | ||
taskId: "4", | ||
reminderDate: { $lte: new Date(testTask.reminderDate.getTime() - 1) }, | ||
user: { $in: [testTask.user] }, | ||
isComplete: false, | ||
}).exec(); | ||
|
||
assert(retrievedTasks.length === 0); | ||
}); | ||
|
||
it("does not retrieve tasks which are completed", async function () { | ||
const testTask = new TaskModel({ | ||
user: "[email protected]", | ||
taskId: "5", | ||
title: "title", | ||
description: "Re-Doing all the things", | ||
location: "science building", | ||
priority: 0, | ||
duration: 120, | ||
reminderDate: "2020-07-14T07:50:00+12:00", | ||
startDate: "2020-08-14T08:50:00+12:00", | ||
endDate: "2020-08-14T07:50:00+12:00", | ||
isComplete: true, | ||
}); | ||
await testTask.save(); | ||
const retrievedTasks = await TaskModel.find({ | ||
taskId: "5", | ||
reminderDate: { $lte: new Date(testTask.reminderDate) }, | ||
user: { $in: [testTask.user] }, | ||
isComplete: false, | ||
}).exec(); | ||
|
||
assert(retrievedTasks.length === 0); | ||
}); | ||
}); |
Oops, something went wrong.