add notification backend and unit test

add notification init frontend
This commit is contained in:
Henrique Santos 2016-01-21 18:42:50 -02:00
parent d5d5ba9066
commit 476eaa8b84
12 changed files with 183 additions and 26 deletions

View file

@ -0,0 +1,15 @@
NotificationsHandler = require("./NotificationsHandler")
logger = require("logger-sharelatex")
module.exports =
getAllUnreadNotifications: (req, res)->
NotificationsHandler.getUserNotifications req.session.user._id, (err, unreadNotifications)->
res.send(unreadNotifications)
markNotificationAsRead: (req, res)->
user_id = req.session.user._id
notification_id = req.params.notification_id
NotificationsHandler.markAsRead user_id, notification_id, ->
res.send()
logger.log user_id:user_id, notification_id:notification_id, "mark notification as read"

View file

@ -0,0 +1,29 @@
settings = require("settings-sharelatex")
request = require("request")
logger = require("logger-sharelatex")
oneSecond = 1000
module.exports =
getUserNotifications: (user_id, callback)->
opts =
uri: "#{settings.apis.notifications.url}/user/#{user_id}"
json: true
timeout: 2000
request.get opts, (err, res, unreadNotifications)->
statusCode = if res? then res.statusCode else 500
if err? or statusCode != 200
e = new Error("something went wrong getting notifications, #{err}, #{statusCode}")
logger.err err:err
callback(e, [])
else
if !unreadNotifications?
unreadNotifications = []
callback(null, unreadNotifications)
markAsRead: (user_id, notification_id, callback)->
opts =
uri: "#{settings.apis.notifications.url}/user/#{user_id}/notification/#{notification_id}"
timeout:oneSecond
logger.log user_id:user_id, notification_id:notification_id, "send mark notification to notifications api"
request.del opts, callback

View file

@ -8,6 +8,7 @@ metrics = require('../../infrastructure/Metrics')
Project = require('../../models/Project').Project
User = require('../../models/User').User
TagsHandler = require("../Tags/TagsHandler")
NotificationsHandler = require("../Notifications/NotificationsHandler")
SubscriptionLocator = require("../Subscription/SubscriptionLocator")
LimitationsManager = require("../Subscription/LimitationsManager")
_ = require("underscore")
@ -16,8 +17,6 @@ SecurityManager = require("../../managers/SecurityManager")
fs = require "fs"
InactiveProjectManager = require("../InactiveData/InactiveProjectManager")
ProjectUpdateHandler = require("./ProjectUpdateHandler")
ReferencesSearchHandler = require("../ReferencesSearch/ReferencesSearchHandler")
module.exports = ProjectController =
@ -127,6 +126,8 @@ module.exports = ProjectController =
async.parallel {
tags: (cb)->
TagsHandler.getAllTags user_id, cb
notifications: (cb)->
NotificationsHandler.getUserNotifications user_id, cb
projects: (cb)->
Project.findAllUsersProjects user_id, 'name lastUpdated publicAccesLevel archived owner_ref', cb
hasSubscription: (cb)->
@ -139,6 +140,7 @@ module.exports = ProjectController =
return next(err)
logger.log results:results, user_id:user_id, "rendering project list"
tags = results.tags[0]
notifications = results.notifications
projects = ProjectController._buildProjectList results.projects[0], results.projects[1], results.projects[2]
user = results.user
ProjectController._injectProjectOwners projects, (error, projects) ->
@ -149,6 +151,7 @@ module.exports = ProjectController =
priority_title: true
projects: projects
tags: tags
notifications: notifications
user: user
hasSubscription: results.hasSubscription[0]
}
@ -170,15 +173,15 @@ module.exports = ProjectController =
return res.render("general/closed", {title:"updating_site"})
if req.session.user?
user_id = req.session.user._id
user_id = req.session.user._id
anonymous = false
else
anonymous = true
user_id = 'openUser'
project_id = req.params.Project_id
logger.log project_id:project_id, "loading editor"
async.parallel {
project: (cb)->
Project.findPopulatedById project_id, cb
@ -195,7 +198,7 @@ module.exports = ProjectController =
SubscriptionLocator.getUsersSubscription user_id, cb
activate: (cb)->
InactiveProjectManager.reactivateProjectIfRequired project_id, cb
markAsOpened: (cb)->
markAsOpened: (cb)->
#don't need to wait for this to complete
ProjectUpdateHandler.markAsOpened project_id, ->
cb()
@ -207,7 +210,6 @@ module.exports = ProjectController =
user = results.user
subscription = results.subscription
daysSinceLastUpdated = (new Date() - project.lastUpdated) /86400000
logger.log project_id:project_id, daysSinceLastUpdated:daysSinceLastUpdated, "got db results for loading editor"
@ -217,10 +219,6 @@ module.exports = ProjectController =
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
allowedFreeTrial = !!subscription.freeTrial.allowed || true
# HACK: don't do it for now
ReferencesSearchHandler.indexProjectReferences project, -> # don't need to wait on this
logger.log project_id:project_id, "rendering editor page"
res.render 'project/editor',
title: project.name
@ -318,3 +316,4 @@ do generateThemeList = () ->
if file.slice(-2) == "js" and file.match(/^theme-/)
cleanName = file.slice(0,-3).slice(6)
THEME_LIST.push cleanName

View file

@ -16,6 +16,7 @@ ReferalController = require('./Features/Referal/ReferalController')
ReferalMiddleware = require('./Features/Referal/ReferalMiddleware')
AuthenticationController = require('./Features/Authentication/AuthenticationController')
TagsController = require("./Features/Tags/TagsController")
NotificationsController = require("./Features/Notifications/NotificationsController")
CollaboratorsRouter = require('./Features/Collaborators/CollaboratorsRouter')
UserInfoController = require('./Features/User/UserInfoController')
UserController = require("./Features/User/UserController")
@ -37,7 +38,6 @@ RateLimiterMiddlewear = require('./Features/Security/RateLimiterMiddlewear')
RealTimeProxyRouter = require('./Features/RealTimeProxy/RealTimeProxyRouter')
InactiveProjectController = require("./Features/InactiveData/InactiveProjectController")
ContactRouter = require("./Features/Contacts/ContactRouter")
ReferencesSearchController = require('./Features/ReferencesSearch/ReferencesSearchController')
logger = require("logger-sharelatex")
_ = require("underscore")
@ -47,7 +47,7 @@ module.exports = class Router
if !Settings.allowPublicAccess
webRouter.all '*', AuthenticationController.requireGlobalLogin
webRouter.get '/login', UserPagesController.loginPage
AuthenticationController.addEndpointToLoginWhitelist '/login'
@ -68,18 +68,18 @@ module.exports = class Router
StaticPagesRouter.apply(webRouter, apiRouter)
RealTimeProxyRouter.apply(webRouter, apiRouter)
ContactRouter.apply(webRouter, apiRouter)
Modules.applyRouter(webRouter, apiRouter)
if Settings.enableSubscriptions
webRouter.get '/user/bonus', AuthenticationController.requireLogin(), ReferalMiddleware.getUserReferalId, ReferalController.bonus
webRouter.get '/blog', BlogController.getIndexPage
webRouter.get '/blog/*', BlogController.getPage
webRouter.get '/user/activate', UserPagesController.activateAccountPage
webRouter.get '/user/settings', AuthenticationController.requireLogin(), UserPagesController.settingsPage
webRouter.post '/user/settings', AuthenticationController.requireLogin(), UserController.updateUserSettings
webRouter.post '/user/password/update', AuthenticationController.requireLogin(), UserController.changePassword
@ -134,6 +134,9 @@ module.exports = class Router
webRouter.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
webRouter.post '/project/:project_id/tag', AuthenticationController.requireLogin(), TagsController.processTagsUpdate
webRouter.get '/notifications', AuthenticationController.requireLogin(), NotificationsController.getAllUnreadNotifications
webRouter.delete '/notifications/:notification_id', AuthenticationController.requireLogin(), NotificationsController.markNotificationAsRead
# Deprecated in favour of /internal/project/:project_id but still used by versioning
apiRouter.get '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails
@ -159,7 +162,7 @@ module.exports = class Router
apiRouter.post '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.mergeUpdate
apiRouter.delete '/user/:user_id/update/*', AuthenticationController.httpAuth, TpdsController.deleteUpdate
apiRouter.post '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.updateProjectContents
apiRouter.delete '/project/:project_id/contents/*', AuthenticationController.httpAuth, TpdsController.deleteProjectContents
@ -168,12 +171,9 @@ module.exports = class Router
webRouter.get "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.getMessages
webRouter.post "/project/:Project_id/messages", SecurityManager.requestCanAccessProject, ChatController.sendMessage
webRouter.get /learn(\/.*)?/, WikiController.getPage
webRouter.post "/project/:Project_id/references", SecurityManager.requestCanAccessProject, ReferencesSearchController.indexFile
webRouter.get "/project/:Project_id/references/keys", SecurityManager.requestCanAccessProject, ReferencesSearchController.getKeys
#Admin Stuff
webRouter.get '/admin', SecurityManager.requestIsAdmin, AdminController.index
webRouter.get '/admin/user', SecurityManager.requestIsAdmin, (req, res)-> res.redirect("/admin/register") #this gets removed by admin-panel addon
@ -192,7 +192,7 @@ module.exports = class Router
apiRouter.get '/status', (req,res)->
res.send("websharelatex is up")
webRouter.get '/health_check', HealthCheckController.check
webRouter.get '/health_check/redis', HealthCheckController.checkRedis

View file

@ -9,7 +9,8 @@ block content
script(type="text/javascript").
window.data = {
projects: !{JSON.stringify(projects).replace(/\//g, '\\/')},
tags: !{JSON.stringify(tags).replace(/\//g, '\\/')}
tags: !{JSON.stringify(tags).replace(/\//g, '\\/')},
notifications: !{JSON.stringify(notifications).replace(/\//g, '\\/')}
};
window.algolia = {
institutions: {
@ -19,6 +20,7 @@ block content
};
.content.content-alt(ng-controller="ProjectPageController")
include ./list/notifications
.container
.row(ng-cloak)
span(ng-show="first_sign_up == 'default' || projects.length > 0")

View file

@ -0,0 +1,18 @@
.notifications(ng-controller="NotificationsController")
.row.row-spaced
.col-xs-12
ul.list-unstyled.notifications-list.structured-list(
select-all-list,
ng-if="notifications.length > 0",
ng-cloak
)
li.notification_entry.container-fluid(
ng-repeat="unreadNotification in notifications",
)
.row
.col-xs-6
span {{unreadNotification._id}}
.col-xs-2
span.owner {{unreadNotification.user_id}}
.col-xs-4
span.last-modified {{unreadNotification.templateKey}}

View file

@ -106,6 +106,8 @@ module.exports =
url: "http://localhost:3036"
sixpack:
url: ""
notifications:
url: "http://localhost:3033"
templates:
user_id: process.env.TEMPLATES_USER_ID or "5395eb7aad1f29a88756c7f2"
@ -143,8 +145,6 @@ module.exports =
versioning: true
compileTimeout: 60
compileGroup: "standard"
references: true
templates: true
plans: plans = [{
planCode: "personal"

View file

@ -2,6 +2,7 @@ define [
"main/project-list/project-list"
"main/project-list/modal-controllers"
"main/project-list/tag-controllers"
"main/project-list/notifications-controller"
"main/project-list/queued-http"
"main/project-list/left-hand-menu-promo-controller"
], () ->

View file

@ -0,0 +1,5 @@
define [
"base"
], (App) ->
App.controller "NotificationsController", ($scope) ->

View file

@ -5,6 +5,7 @@ define [
App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp, event_tracking, $timeout, sixpack) ->
$scope.projects = window.data.projects
$scope.tags = window.data.tags
$scope.notifications = window.data.notifications
$scope.allSelected = false
$scope.selectedProjects = []
$scope.filter = "all"

View file

@ -0,0 +1,39 @@
SandboxedModule = require('sandboxed-module')
assert = require('assert')
require('chai').should()
sinon = require('sinon')
modulePath = require('path').join __dirname, '../../../../app/js/Features/Notifications/NotificationsController.js'
describe 'NotificationsController', ->
user_id = "123nd3ijdks"
notification_id = "123njdskj9jlk"
beforeEach ->
@handler =
getUserNotifications: sinon.stub().callsArgWith(1)
markAsRead: sinon.stub().callsArgWith(2)
@controller = SandboxedModule.require modulePath, requires:
"./NotificationsHandler":@handler
'logger-sharelatex':
log:->
err:->
@req =
params:
notification_id:notification_id
session:
user:
_id:user_id
it 'should ask the handler for all unread notifications', (done)->
allNotifications = [{_id: notification_id, user_id: user_id}]
@handler.getUserNotifications = sinon.stub().callsArgWith(1, null, allNotifications)
@controller.getAllUnreadNotifications @req, send:(body)=>
body.should.equal allNotifications
@handler.getUserNotifications.calledWith(user_id).should.equal true
done()
it 'should send a delete request when a delete has been received to mark a notification', (done)->
@controller.markNotificationAsRead @req, send:=>
@handler.markAsRead.calledWith(user_id, notification_id).should.equal true
done()

View file

@ -0,0 +1,48 @@
SandboxedModule = require('sandboxed-module')
assert = require('chai').assert
require('chai').should()
sinon = require('sinon')
modulePath = require('path').join __dirname, '../../../../app/js/Features/Notifications/NotificationsHandler.js'
_ = require('underscore')
describe 'NotificationsHandler', ->
user_id = "123nd3ijdks"
notification_id = "123njdskj9jlk"
notificationUrl = "notification.sharelatex.testing"
beforeEach ->
@request =
post: sinon.stub().callsArgWith(1)
del: sinon.stub().callsArgWith(1)
get: sinon.stub()
@handler = SandboxedModule.require modulePath, requires:
"settings-sharelatex": apis:{notifications:{url:notificationUrl}}
"request":@request
'logger-sharelatex':
log:->
err:->
describe "getUserNotifications", ->
it 'should get unread notifications', (done)->
stubbedNotifications = [{_id: notification_id, user_id: user_id}]
@request.get.callsArgWith(1, null, {statusCode:200}, stubbedNotifications)
@handler.getUserNotifications user_id, (err, unreadNotifications)=>
stubbedNotifications.should.deep.equal unreadNotifications
getOpts =
uri: "#{notificationUrl}/user/#{user_id}"
json:true
timeout:2000
@request.get.calledWith(getOpts).should.equal true
done()
it 'should return empty arrays if there are no notifications', ->
@request.get.callsArgWith(1, null, {statusCode:200}, null)
@handler.getUserNotifications user_id, (err, unreadNotifications)=>
unreadNotifications.length.should.equal 0
describe "markAsRead", ->
it 'should send a delete request when a delete has been received to mark a notification', (done)->
@handler.markAsRead user_id, notification_id, =>
@request.del.calledWith({uri:"#{notificationUrl}/user/#{user_id}/notification/#{notification_id}", timeout:1000}).should.equal true
done()