commit 1e60327f2edfd83ea98cea9de2a222647b56342f Author: Henrique Santos Date: Thu Jan 14 10:35:16 2016 -0200 create scaffolding base on tags repo diff --git a/services/notifications/.gitignore b/services/notifications/.gitignore new file mode 100644 index 0000000000..c9157bfdad --- /dev/null +++ b/services/notifications/.gitignore @@ -0,0 +1,74 @@ +Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite + +# OS generated files # +###################### +.DS_Store? +ehthumbs.db +Icon? +Thumbs.db + +node_modules/* +data/* + +app.js +example.js +app/js/* +test/unit/js/* +test/smoke/js/* +cookies.txt +requestQueueWorker.js +TpdsWorker.js +BackgroundJobsWorker.js +UserAndProjectPopulator.coffee + +public/js/history/versiondetail.js +!public/js/libs/ +public/js/* +!public/js/ace/* +!public/js/libs/ +public/js/libs/sharejs.js +public/js/editor.js +public/js/home.js +public/js/forms.js +public/js/gui.js +public/js/admin.js +public/js/history/* +public/stylesheets/style.css +public/brand/plans.css +public/minjs/ + +public/js/main.js +Gemfile.lock + +*.swp +.DS_Store + +app/views/external + +/modules/ diff --git a/services/notifications/Gruntfile.coffee b/services/notifications/Gruntfile.coffee new file mode 100644 index 0000000000..9bf2f98681 --- /dev/null +++ b/services/notifications/Gruntfile.coffee @@ -0,0 +1,52 @@ +module.exports = (grunt) -> + grunt.initConfig + coffee: + app_src: + expand: true, + cwd: "app/coffee" + src: ['**/*.coffee'], + dest: 'app/js/', + ext: '.js' + + app: + src: "app.coffee" + dest: "app.js" + + unit_tests: + expand: true + cwd: "test/unit/coffee" + src: ["**/*.coffee"] + dest: "test/unit/js/" + ext: ".js" + + clean: + app: ["app/js/"] + unit_tests: ["test/unit/js"] + + execute: + app: + src: "app.js" + + mochaTest: + unit: + options: + reporter: grunt.option('reporter') or 'spec' + src: ["test/unit/js/**/*.js"] + + grunt.loadNpmTasks 'grunt-contrib-coffee' + grunt.loadNpmTasks 'grunt-contrib-clean' + grunt.loadNpmTasks 'grunt-mocha-test' + grunt.loadNpmTasks 'grunt-execute' + grunt.loadNpmTasks 'grunt-bunyan' + + grunt.registerTask 'compile:app', ['clean:app', 'coffee:app', 'coffee:app_src'] + grunt.registerTask 'run', ['compile:app', 'bunyan', 'execute'] + + grunt.registerTask 'compile:unit_tests', ['clean:unit_tests', 'coffee:unit_tests'] + grunt.registerTask 'test:unit', ['compile:app', 'compile:unit_tests', 'mochaTest:unit'] + + grunt.registerTask 'install', 'compile:app' + + grunt.registerTask 'default', ['run'] + + diff --git a/services/notifications/README.md b/services/notifications/README.md new file mode 100644 index 0000000000..02e1a15e78 --- /dev/null +++ b/services/notifications/README.md @@ -0,0 +1,4 @@ +notifications-sharelatex +=============== + +An API for managing user notifications in ShareLaTeX diff --git a/services/notifications/app.coffee b/services/notifications/app.coffee new file mode 100644 index 0000000000..96f91d645e --- /dev/null +++ b/services/notifications/app.coffee @@ -0,0 +1,44 @@ +Settings = require 'settings-sharelatex' +logger = require 'logger-sharelatex' +logger.initialize("notifications-sharelatex") +express = require('express') +app = express() +controller = require("./app/js/NotificationController") +mongojs = require('mongojs') +db = mongojs(Settings.mongo.url, ['notifications']) +Path = require("path") +metrics = require("metrics-sharelatex") +metrics.initialize("notifications") +metrics.mongodb.monitor(Path.resolve(__dirname + "/node_modules/mongojs/node_modules/mongodb"), logger) +metrics.memory.monitor(logger) + +HealthCheckController = require("./app/js/HealthCheckController") + +app.configure ()-> + app.use express.methodOverride() + app.use express.bodyParser() + app.use metrics.http.monitor(logger) + app.use express.errorHandler() + +app.post '/user/:user_id', controller.addNotification +app.get '/user/:user_id', controller.getUsersNotifications +app.del '/user/:user_id/notification_id/:notification_id', controller.removeNotification + +app.get '/status', (req, res)-> + res.send('notifications sharelatex up') + +app.get '/health_check', (req, res)-> + HealthCheckController.check (err)-> + if err? + logger.err err:err, "error performing health check" + res.send 500 + else + res.send 200 + +app.get '*', (req, res)-> + res.send 404 + +host = Settings.internal?.notifications?.host || "localhost" +port = Settings.internal?.notifications?.port || 3033 +app.listen port, host, -> + logger.info "notifications starting up, listening on #{host}:#{port}" diff --git a/services/notifications/app/coffee/HealthCheckController.coffee b/services/notifications/app/coffee/HealthCheckController.coffee new file mode 100644 index 0000000000..81fc1f55e6 --- /dev/null +++ b/services/notifications/app/coffee/HealthCheckController.coffee @@ -0,0 +1,40 @@ +ObjectId = require("mongojs").ObjectId +request = require("request") +async = require("async") +_ = require("underscore") +settings = require("settings-sharelatex") +port = settings.internal.notifications.port +logger = require "logger-sharelatex" + + +module.exports = + check : (callback)-> + project_id = ObjectId() + user_id = ObjectId(settings.notifications.healthCheck.user_id) + notification_key = "smoke-test-notification" + getOpts = (endPath)-> {url:"http://localhost:#{port}/user/#{user_id}#{endPath}", timeout:3000} + logger.log user_id:user_id, opts:getOpts(), key:notification_key, user_id:user_id, "running health check" + jobs = [ + (cb)-> + opts = getOpts("/") + opts.json = {key: notification_key, messageOpts:'', templateKey:'', user_id:user_id} + request.post(opts, cb) + (cb)-> + opts = getOpts("/") + opts.json = true + request.get opts, (err, res, body)-> + if res.statusCode != 200 + return cb("status code not 200, its #{res.statusCode}") + + hasNotification = _.some body, (notification)-> + notification.key == notification_key and _.contains(notification.user_id, user_id.toString()) + if hasNotification + cb() + else + cb("notification not found in response") + ] + async.series jobs, (err)-> + if err? + callback(err) + opts = getOpts("/notification_id/#{notification_id}") + request.del opts, callback diff --git a/services/notifications/app/coffee/Notifications.coffee b/services/notifications/app/coffee/Notifications.coffee new file mode 100644 index 0000000000..d89edbfe94 --- /dev/null +++ b/services/notifications/app/coffee/Notifications.coffee @@ -0,0 +1,26 @@ +Settings = require 'settings-sharelatex' +logger = require('logger-sharelatex') +mongojs = require('mongojs') +db = mongojs(Settings.mongo?.url, ['notifications']) + +module.exports = + + getUserUnreadNotifications: (user_id, callback = (err, user)->)-> + db.notifications.find {"user_id" : user_id}, (err, user)-> + callback err, user + + addNotification: (user_id, notification, callback)-> + doc = + user_id: user_id + key: notification.key + messageOpts: notification.messageOpts + templateKey: notification.templateKey + db.notifications.insert(doc, callback) + + removeNotification: (user_id, notification_key, callback)-> + searchOps = + user_id:user_id + key:notification_key + updateOperation = + "$set": {read:true} + db.notifications.update searchOps, updateOperation, callback diff --git a/services/notifications/app/coffee/NotificationsController.coffee b/services/notifications/app/coffee/NotificationsController.coffee new file mode 100644 index 0000000000..52772ccbaf --- /dev/null +++ b/services/notifications/app/coffee/NotificationsController.coffee @@ -0,0 +1,19 @@ +Notifications = require("./Notifications") +logger = require("logger-sharelatex") + +module.exports = + + getUserNotifications: (req, res)-> + logger.log user_id: req.params.user_id, "getting user unread notifications" + Notifications.getUserUnreadNotifications req.params.user_id, (err, notifications)-> + res.json(notifications) + + addNotification: (req, res)-> + logger.log user_id: req.params.user_id, notification:req.body, "adding notification" + Notifications.addNotification req.params.user_id, req.body, (err, notifications)-> + res.send() + + removeNotificacion: (req, res)-> + logger.log user_id: req.params.user_id, notification_key:req.params.key "removing notification" + Notifications.removeNotification req.params.user_id, req.params.key, (err, notifications)-> + res.send() diff --git a/services/notifications/config/settings.defaults.coffee b/services/notifications/config/settings.defaults.coffee new file mode 100644 index 0000000000..d2f8db25ef --- /dev/null +++ b/services/notifications/config/settings.defaults.coffee @@ -0,0 +1,14 @@ +module.exports = Settings = + internal: + notifications: + port: 3033 + host: "localhost" + + mongo: + url : 'mongodb://127.0.0.1/sharelatex' + + notifications: + healthCheck: + user_id: "5620bece05509b0a7a3cbc62" + + diff --git a/services/notifications/package.json b/services/notifications/package.json new file mode 100644 index 0000000000..7e5761a225 --- /dev/null +++ b/services/notifications/package.json @@ -0,0 +1,34 @@ +{ + "name": "notifications-sharelatex", + "version": "0.0.1", + "description": "An API to handle user notifications", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "async": "^0.1.22", + "express": "3.1.0", + "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.1.0", + "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.3.0", + "mongojs": "1.3.0", + "node-statsd": "0.0.3", + "request": "^2.65.0", + "settings-sharelatex": "git+https://github.com/sharelatex/settings-sharelatex.git#v1.0.0", + "underscore": "1.4.4" + }, + "devDependencies": { + "bunyan": "^1.0.0", + "chai": "", + "grunt": "^0.4.5", + "grunt-bunyan": "^0.5.0", + "grunt-contrib-clean": "^0.6.0", + "grunt-contrib-coffee": "^0.11.0", + "grunt-execute": "^0.2.2", + "grunt-mocha-test": "^0.11.0", + "sandboxed-module": "", + "sinon": "" + } +} diff --git a/services/notifications/test/unit/coffee/NotificationsControllerTest.coffee b/services/notifications/test/unit/coffee/NotificationsControllerTest.coffee new file mode 100644 index 0000000000..d959e72380 --- /dev/null +++ b/services/notifications/test/unit/coffee/NotificationsControllerTest.coffee @@ -0,0 +1,11 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +modulePath = "../../../app/js/NotificationsController.js" +SandboxedModule = require('sandboxed-module') +assert = require('assert') + +user_id = "51dc93e6fb625a261300003b" +notification_key = '123434' + +describe 'Notifications controller', -> diff --git a/services/notifications/test/unit/coffee/NotificationsTests.coffee b/services/notifications/test/unit/coffee/NotificationsTests.coffee new file mode 100644 index 0000000000..998b77599a --- /dev/null +++ b/services/notifications/test/unit/coffee/NotificationsTests.coffee @@ -0,0 +1,30 @@ +sinon = require('sinon') +chai = require('chai') +should = chai.should() +modulePath = "../../../app/js/Notifications.js" +SandboxedModule = require('sandboxed-module') +assert = require('assert') + +user_id = "51dc93e6fb625a261300003b" +notification_key = '123434' + +describe 'creating a user', -> + beforeEach -> + self = @ + @findOneStub = sinon.stub() + @findStub = sinon.stub() + @saveStub = sinon.stub() + @updateStub = sinon.stub() + + @mongojs = => + notifications: + update: self.mongojsUpdate + find: @findStub + findOne: @findOneStub + save: @saveStub + update: @updateStub + + @repository = SandboxedModule.require modulePath, requires: + 'logger-sharelatex': log:-> + 'settings-sharelatex': {} + 'mongojs':@mongojs