diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee index 05792679a6..1977a4e01d 100644 --- a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee +++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteController.coffee @@ -50,6 +50,16 @@ module.exports = CollaboratorsInviteController = return next(err) res.sendStatus(201) + resendInvite: (req, res, next) -> + projectId = req.params.Project_id + inviteId = req.params.invite_id + logger.log {projectId, inviteId}, "resending invite" + CollaboratorsInviteHandler.resendInvite projectId, inviteId, (err) -> + if err? + logger.err {projectId, inviteId}, "error revoking invite" + return next(err) + res.sendStatus(201) + viewInvite: (req, res, next) -> projectId = req.params.Project_id token = req.params.token diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee index 25008cfde7..83e3c2bb18 100644 --- a/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee +++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsInviteHandler.coffee @@ -54,6 +54,18 @@ module.exports = CollaboratorsInviteHandler = return callback(err) callback(null) + resendInvite: (projectId, inviteId, callback=(err)->) -> + logger.log {projectId, inviteId}, "resending invite email" + ProjectInvite.findOne {_id: inviteId, projectId: projectId}, (err, invite) -> + if err? + logger.err {err, projectId, inviteId}, "error finding invite" + return callback(err) + if !invite? + logger.err {err, projectId, inviteId}, "no invite found, nothing to resend" + return callback(null) + CollaboratorsEmailHandler.notifyUserOfProjectInvite projectId, invite.email, invite + callback(null) + getInviteByToken: (projectId, tokenString, callback=(err,invite)->) -> logger.log {projectId, tokenString}, "fetching invite by token" ProjectInvite.findOne {projectId: projectId, token: tokenString}, (err, invite) -> diff --git a/services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee b/services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee index f67bb3ee6c..0da728542e 100644 --- a/services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee +++ b/services/web/app/coffee/Features/Collaborators/CollaboratorsRouter.coffee @@ -29,6 +29,12 @@ module.exports = CollaboratorsInviteController.revokeInvite ) + webRouter.post( + '/project/:Project_id/invite/:invite_id/resend', + AuthorizationMiddlewear.ensureUserCanAdminProject, + CollaboratorsInviteController.resendInvite + ) + webRouter.get( '/project/:Project_id/invite/token/:token', AuthenticationController.requireLogin(), diff --git a/services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee b/services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee index 942db3e71c..a368bbb4f2 100644 --- a/services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee +++ b/services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee @@ -136,7 +136,15 @@ define [ $scope.state.error = "Sorry, something went wrong :(" $scope.resendInvite = (invite) -> - console.log ">> resend" + $scope.state.error = null + $scope.state.inflight = true + projectInvites + .resendInvite(invite._id) + .success () -> + $scope.state.inflight = false + .error () -> + $scope.state.inflight = false + $scope.state.error = "Sorry, something went wrong resending the invite :(" $scope.openMakePublicModal = () -> $modal.open { diff --git a/services/web/public/coffee/ide/share/services/projectInvites.coffee b/services/web/public/coffee/ide/share/services/projectInvites.coffee index f453792574..cad5122386 100644 --- a/services/web/public/coffee/ide/share/services/projectInvites.coffee +++ b/services/web/public/coffee/ide/share/services/projectInvites.coffee @@ -19,6 +19,11 @@ define [ "X-Csrf-Token": window.csrfToken }) + resendInvite: (inviteId, privileges) -> + $http.post("/project/#{ide.project_id}/invite/#{inviteId}/resend", { + _csrf: window.csrfToken + }) + getInvites: () -> $http.get("/project/#{ide.project_id}/invite", { json: true diff --git a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee index 4cfac39232..7ac3d784c0 100644 --- a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteControllerTests.coffee @@ -429,6 +429,50 @@ describe "CollaboratorsInviteController", -> it 'should call ProjectGetter.getProject', -> @ProjectGetter.getProject.callCount.should.equal 1 + describe "resendInvite", -> + + beforeEach -> + @req.params = + Project_id: @project_id + invite_id: @invite_id = "thuseoautoh" + @req.session = + user: _id: @current_user_id = "current-user-id" + @res.render = sinon.stub() + @res.sendStatus = sinon.stub() + @CollaboratorsInviteHandler.resendInvite = sinon.stub().callsArgWith(2, null) + @callback = sinon.stub() + @next = sinon.stub() + + describe 'when resendInvite does not produce an error', -> + + beforeEach -> + @CollaboratorsInviteController.resendInvite @req, @res, @next + + it 'should produce a 201 response', -> + @res.sendStatus.callCount.should.equal 1 + @res.sendStatus.calledWith(201).should.equal true + + it 'should have called resendInvite', -> + @CollaboratorsInviteHandler.resendInvite.callCount.should.equal 1 + + describe 'when resendInvite produces an error', -> + + beforeEach -> + @err = new Error('woops') + @CollaboratorsInviteHandler.resendInvite = sinon.stub().callsArgWith(2, @err) + @CollaboratorsInviteController.resendInvite @req, @res, @next + + it 'should not produce a 201 response', -> + @res.sendStatus.callCount.should.equal 0 + + it 'should call next with the error', -> + @next.callCount.should.equal 1 + @next.calledWith(@err).should.equal true + + it 'should have called resendInvite', -> + @CollaboratorsInviteHandler.resendInvite.callCount.should.equal 1 + + describe "revokeInvite", -> beforeEach -> diff --git a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteHandlerTests.coffee b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteHandlerTests.coffee index 4e9a2cc95a..0e8ceef0b3 100644 --- a/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Collaborators/CollaboratorsInviteHandlerTests.coffee @@ -209,6 +209,67 @@ describe "CollaboratorsInviteHandler", -> expect(err).to.be.instanceof Error done() + describe 'resendInvite', -> + + beforeEach -> + @ProjectInvite.findOne.callsArgWith(1, null, @fakeInvite) + @CollaboratorsEmailHandler.notifyUserOfProjectInvite = sinon.stub() + @call = (callback) => + @CollaboratorsInviteHandler.resendInvite @projectId, @inviteId, callback + + describe 'when all goes well', -> + + beforeEach -> + + it 'should not produce an error', (done) -> + @call (err) => + expect(err).to.not.be.instanceof Error + expect(err).to.be.oneOf [null, undefined] + done() + + it 'should call ProjectInvite.findOne', (done) -> + @call (err, invite) => + @ProjectInvite.findOne.callCount.should.equal 1 + @ProjectInvite.findOne.calledWith({_id: @inviteId, projectId: @projectId}).should.equal true + done() + + it 'should have called CollaboratorsEmailHandler.notifyUserOfProjectInvite', (done) -> + @call (err, invite) => + @CollaboratorsEmailHandler.notifyUserOfProjectInvite.callCount.should.equal 1 + @CollaboratorsEmailHandler.notifyUserOfProjectInvite.calledWith(@projectId, @email).should.equal true + done() + + describe 'when findOne produces an error', -> + + beforeEach -> + @ProjectInvite.findOne.callsArgWith(1, new Error('woops')) + + it 'should produce an error', (done) -> + @call (err, invite) => + expect(err).to.be.instanceof Error + done() + + it 'should not have called CollaboratorsEmailHandler.notifyUserOfProjectInvite', (done) -> + @call (err, invite) => + @CollaboratorsEmailHandler.notifyUserOfProjectInvite.callCount.should.equal 0 + done() + + describe 'when findOne does not find an invite', -> + + beforeEach -> + @ProjectInvite.findOne.callsArgWith(1, null, null) + + it 'should not produce an error', (done) -> + @call (err, invite) => + expect(err).to.not.be.instanceof Error + expect(err).to.be.oneOf [null, undefined] + done() + + it 'should not have called CollaboratorsEmailHandler.notifyUserOfProjectInvite', (done) -> + @call (err, invite) => + @CollaboratorsEmailHandler.notifyUserOfProjectInvite.callCount.should.equal 0 + done() + describe 'getInviteByToken', -> beforeEach ->