diff --git a/services/web/app/coffee/Features/BetaProgram/BetaProgramController.coffee b/services/web/app/coffee/Features/BetaProgram/BetaProgramController.coffee index 1fc7ee4d12..a986eb8e4c 100644 --- a/services/web/app/coffee/Features/BetaProgram/BetaProgramController.coffee +++ b/services/web/app/coffee/Features/BetaProgram/BetaProgramController.coffee @@ -14,11 +14,21 @@ module.exports = BetaProgramController = BetaProgramHandler.optIn user_id, (err) -> if err return next(err) - return res.redirect "/beta/opt-in" + return res.redirect "/beta/participate" + + optOut: (req, res, next) -> + user_id = req?.session?.user?._id + logger.log {user_id}, "user opting out of beta program" + if !user_id + return next(new Error("no user id in session")) + BetaProgramHandler.optOut user_id, (err) -> + if err + return next(err) + return res.redirect "/beta/participate" optInPage: (req, res, next)-> user_id = req.session?.user?._id - logger.log {user_id}, "showing beta opt-in page for user" + logger.log {user_id}, "showing beta participation page for user" UserLocator.findById user_id, (err, user)-> if err logger.err {err, user_id}, "error fetching user" diff --git a/services/web/app/coffee/Features/BetaProgram/BetaProgramHandler.coffee b/services/web/app/coffee/Features/BetaProgram/BetaProgramHandler.coffee index 2851ff3fe8..788782d578 100644 --- a/services/web/app/coffee/Features/BetaProgram/BetaProgramHandler.coffee +++ b/services/web/app/coffee/Features/BetaProgram/BetaProgramHandler.coffee @@ -16,3 +16,16 @@ module.exports = BetaProgramHandler = logger.err {err, user_id}, "problem adding user to beta" return callback(err) return callback(null) + + optOut: (user_id, callback=(err)->) -> + User.findById user_id, (err, user) -> + if err + logger.err {err, user_id}, "problem removing user from beta" + return callback(err) + metrics.inc "beta-program.opt-out" + user.betaProgram = false + user.save (err) -> + if err + logger.err {err, user_id}, "problem removing user from beta" + return callback(err) + return callback(null) diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index 38f501cafa..8911b7de2e 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -198,8 +198,9 @@ module.exports = class Router webRouter.post "/project/:Project_id/references/index", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.index webRouter.post "/project/:Project_id/references/indexAll", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.indexAll - webRouter.get "/beta/opt-in", AuthenticationController.requireLogin(), BetaProgramController.optInPage + webRouter.get "/beta/participate", AuthenticationController.requireLogin(), BetaProgramController.optInPage webRouter.post "/beta/opt-in", AuthenticationController.requireLogin(), BetaProgramController.optIn + webRouter.post "/beta/opt-out", AuthenticationController.requireLogin(), BetaProgramController.optOut #Admin Stuff webRouter.get '/admin', AuthorizationMiddlewear.ensureUserIsSiteAdmin, AdminController.index diff --git a/services/web/app/views/beta_program/opt_in.jade b/services/web/app/views/beta_program/opt_in.jade index 5d1f7705f8..40e4c1992b 100644 --- a/services/web/app/views/beta_program/opt_in.jade +++ b/services/web/app/views/beta_program/opt_in.jade @@ -27,8 +27,15 @@ block content .col-md-12 if user.betaProgram p #{translate("beta_program_already_participating")} - .form-group - a(href="/project").btn.btn-info #{translate("back_to_your_projects")} + form(method="post", action="/beta/opt-out", novalidate) + .form-group + input(type="hidden", name="_csrf", value=csrfToken) + button.btn.btn-primary( + type="submit" + ) + span #{translate("beta_program_opt_out_action")} + .form-group + a(href="/project").btn.btn-info #{translate("back_to_your_projects")} else form(method="post", action="/beta/opt-in", novalidate) .form-group diff --git a/services/web/test/UnitTests/coffee/BetaProgram/BetaProgramControllerTests.coffee b/services/web/test/UnitTests/coffee/BetaProgram/BetaProgramControllerTests.coffee index 86a6ad3801..742eb6db23 100644 --- a/services/web/test/UnitTests/coffee/BetaProgram/BetaProgramControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/BetaProgram/BetaProgramControllerTests.coffee @@ -21,6 +21,7 @@ describe "BetaProgramController", -> @BetaProgramController = SandboxedModule.require modulePath, requires: "./BetaProgramHandler": @BetaProgramHandler = { optIn: sinon.stub() + optOut: sinon.stub() }, "../User/UserLocator": @UserLocator = { findById: sinon.stub() @@ -44,10 +45,10 @@ describe "BetaProgramController", -> beforeEach -> @BetaProgramHandler.optIn.callsArgWith(1, null) - it "should redirect to '/beta/opt-in'", () -> + it "should redirect to '/beta/participate'", () -> @BetaProgramController.optIn @req, @res, @next @res.redirect.callCount.should.equal 1 - @res.redirect.firstCall.args[0].should.equal "/beta/opt-in" + @res.redirect.firstCall.args[0].should.equal "/beta/participate" it "should not call next with an error", () -> @BetaProgramController.optIn @req, @res, @next @@ -66,7 +67,7 @@ describe "BetaProgramController", -> beforeEach -> @BetaProgramHandler.optIn.callsArgWith(1, new Error('woops')) - it "should not redirect to '/'", () -> + it "should not redirect to '/beta/participate'", () -> @BetaProgramController.optIn @req, @res, @next @res.redirect.callCount.should.equal 0 @@ -75,6 +76,43 @@ describe "BetaProgramController", -> @next.callCount.should.equal 1 @next.firstCall.args[0].should.be.instanceof Error + describe "optOut", -> + + beforeEach -> + @BetaProgramHandler.optOut.callsArgWith(1, null) + + it "should redirect to '/beta/participate'", () -> + @BetaProgramController.optOut @req, @res, @next + @res.redirect.callCount.should.equal 1 + @res.redirect.firstCall.args[0].should.equal "/beta/participate" + + it "should not call next with an error", () -> + @BetaProgramController.optOut @req, @res, @next + @next.callCount.should.equal 0 + + it "should not call next with an error", () -> + @BetaProgramController.optOut @req, @res, @next + @next.callCount.should.equal 0 + + it "should call BetaProgramHandler.optOut", () -> + @BetaProgramController.optOut @req, @res, @next + @BetaProgramHandler.optOut.callCount.should.equal 1 + + describe "when BetaProgramHandler.optOut produces an error", -> + + beforeEach -> + @BetaProgramHandler.optOut.callsArgWith(1, new Error('woops')) + + it "should not redirect to '/beta/participate'", () -> + @BetaProgramController.optOut @req, @res, @next + @res.redirect.callCount.should.equal 0 + + it "should produce an error", () -> + @BetaProgramController.optOut @req, @res, @next + @next.callCount.should.equal 1 + @next.firstCall.args[0].should.be.instanceof Error + + describe "optInPage", -> beforeEach -> diff --git a/services/web/test/UnitTests/coffee/BetaProgram/BetaProgramHandlerTests.coffee b/services/web/test/UnitTests/coffee/BetaProgram/BetaProgramHandlerTests.coffee index e8b74a9f06..affa9f38a5 100644 --- a/services/web/test/UnitTests/coffee/BetaProgram/BetaProgramHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/BetaProgram/BetaProgramHandlerTests.coffee @@ -34,6 +34,7 @@ describe 'BetaProgramHandler', -> describe "optIn", -> beforeEach -> + @user.betaProgram = false @call = (callback) => @handler.optIn @user_id, callback @@ -63,3 +64,37 @@ describe 'BetaProgramHandler', -> expect(err).to.not.equal null expect(err).to.be.instanceof Error done() + + describe "optOut", -> + + beforeEach -> + @user.betaProgram = true + @call = (callback) => + @handler.optOut @user_id, callback + + it "should set betaProgram = true on user object", (done) -> + @call (err) => + @user.betaProgram.should.equal false + done() + + it "should call user.save", (done) -> + @call (err) => + @user.save.callCount.should.equal 1 + done() + + it "should not produce an error", (done) -> + @call (err) => + expect(err).to.equal null + expect(err).to.not.be.instanceof Error + done() + + describe "when user.save produces an error", -> + + beforeEach -> + @user.save.callsArgWith(0, new Error('woops')) + + it "should produce an error", (done) -> + @call (err) => + expect(err).to.not.equal null + expect(err).to.be.instanceof Error + done()