mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-23 08:43:54 +00:00
add notification backend and unit test
add notification init frontend
This commit is contained in:
parent
d5d5ba9066
commit
476eaa8b84
12 changed files with 183 additions and 26 deletions
|
@ -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"
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
18
services/web/app/views/project/list/notifications.jade
Normal file
18
services/web/app/views/project/list/notifications.jade
Normal 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}}
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
], () ->
|
|
@ -0,0 +1,5 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
|
||||
App.controller "NotificationsController", ($scope) ->
|
|
@ -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"
|
||||
|
|
|
@ -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()
|
|
@ -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()
|
Loading…
Reference in a new issue