mirror of
https://github.com/overleaf/overleaf.git
synced 2025-02-18 17:41:32 +00:00
Merge pull request #2152 from overleaf/as-per-user-trash-backend
Add per-user trash endpoint GitOrigin-RevId: 94a6e3416b047e1f8721159ac0d049e98785e5ce
This commit is contained in:
parent
9a31361795
commit
9cd5af840a
4 changed files with 134 additions and 29 deletions
|
@ -54,6 +54,7 @@ const Features = require('../../infrastructure/Features')
|
||||||
const BrandVariationsHandler = require('../BrandVariations/BrandVariationsHandler')
|
const BrandVariationsHandler = require('../BrandVariations/BrandVariationsHandler')
|
||||||
const { getUserAffiliations } = require('../Institutions/InstitutionsAPI')
|
const { getUserAffiliations } = require('../Institutions/InstitutionsAPI')
|
||||||
const V1Handler = require('../V1/V1Handler')
|
const V1Handler = require('../V1/V1Handler')
|
||||||
|
const { Project } = require('../../models/Project')
|
||||||
|
|
||||||
module.exports = ProjectController = {
|
module.exports = ProjectController = {
|
||||||
_isInPercentageRollout(rolloutName, objectId, percentage) {
|
_isInPercentageRollout(rolloutName, objectId, percentage) {
|
||||||
|
@ -199,6 +200,38 @@ module.exports = ProjectController = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
trashProject(req, res, next) {
|
||||||
|
const projectId = req.params.project_id
|
||||||
|
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
|
||||||
|
Project.update(
|
||||||
|
{ _id: projectId },
|
||||||
|
{ $addToSet: { trashed: userId } },
|
||||||
|
error => {
|
||||||
|
if (error) {
|
||||||
|
return next(error)
|
||||||
|
}
|
||||||
|
res.sendStatus(200)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
untrashProject(req, res, next) {
|
||||||
|
const projectId = req.params.project_id
|
||||||
|
const userId = AuthenticationController.getLoggedInUserId(req)
|
||||||
|
|
||||||
|
Project.update(
|
||||||
|
{ _id: projectId },
|
||||||
|
{ $pull: { trashed: userId } },
|
||||||
|
error => {
|
||||||
|
if (error) {
|
||||||
|
return next(error)
|
||||||
|
}
|
||||||
|
res.sendStatus(200)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
cloneProject(req, res, next) {
|
cloneProject(req, res, next) {
|
||||||
res.setTimeout(5 * 60 * 1000) // allow extra time for the copy to complete
|
res.setTimeout(5 * 60 * 1000) // allow extra time for the copy to complete
|
||||||
metrics.inc('cloned-project')
|
metrics.inc('cloned-project')
|
||||||
|
|
|
@ -443,6 +443,18 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) {
|
||||||
CompileController.wordCount
|
CompileController.wordCount
|
||||||
)
|
)
|
||||||
|
|
||||||
|
webRouter.post(
|
||||||
|
'/project/:project_id/trash',
|
||||||
|
AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
ProjectController.trashProject
|
||||||
|
)
|
||||||
|
|
||||||
|
webRouter.delete(
|
||||||
|
'/project/:project_id/trash',
|
||||||
|
AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
|
ProjectController.untrashProject
|
||||||
|
)
|
||||||
|
|
||||||
webRouter.delete(
|
webRouter.delete(
|
||||||
'/Project/:Project_id',
|
'/Project/:Project_id',
|
||||||
AuthorizationMiddleware.ensureUserCanAdminProject,
|
AuthorizationMiddleware.ensureUserCanAdminProject,
|
||||||
|
|
|
@ -1,43 +1,102 @@
|
||||||
/* eslint-disable
|
|
||||||
handle-callback-err,
|
|
||||||
max-len,
|
|
||||||
no-unused-vars,
|
|
||||||
*/
|
|
||||||
// TODO: This file was created by bulk-decaffeinate.
|
|
||||||
// Fix any style issues and re-enable lint.
|
|
||||||
/*
|
|
||||||
* decaffeinate suggestions:
|
|
||||||
* DS102: Remove unnecessary code created because of implicit returns
|
|
||||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
||||||
*/
|
|
||||||
const { expect } = require('chai')
|
const { expect } = require('chai')
|
||||||
const async = require('async')
|
const User = require('./helpers/User').promises
|
||||||
const User = require('./helpers/User')
|
const { Project } = require('../../../app/src/models/Project')
|
||||||
|
|
||||||
describe('Project CRUD', function() {
|
describe('Project CRUD', function() {
|
||||||
beforeEach(function(done) {
|
beforeEach(async function() {
|
||||||
this.user = new User()
|
this.user = new User()
|
||||||
return this.user.login(done)
|
await this.user.login()
|
||||||
|
|
||||||
|
this.projectId = await this.user.createProject('example-project')
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(async function() {
|
||||||
|
// TODO: This can be removed after migrations are merged
|
||||||
|
await Project.deleteMany({}).exec()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("when project doesn't exist", function() {
|
describe("when project doesn't exist", function() {
|
||||||
it('should return 404', function(done) {
|
it('should return 404', async function() {
|
||||||
return this.user.request.get(
|
const { response } = await this.user.doRequest(
|
||||||
'/project/aaaaaaaaaaaaaaaaaaaaaaaa',
|
'GET',
|
||||||
(err, res, body) => {
|
'/project/aaaaaaaaaaaaaaaaaaaaaaaa'
|
||||||
expect(res.statusCode).to.equal(404)
|
|
||||||
return done()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
expect(response.statusCode).to.equal(404)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('when project has malformed id', function() {
|
describe('when project has malformed id', function() {
|
||||||
it('should return 404', function(done) {
|
it('should return 404', async function() {
|
||||||
return this.user.request.get('/project/blah', (err, res, body) => {
|
const { response } = await this.user.doRequest('GET', '/project/blah')
|
||||||
expect(res.statusCode).to.equal(404)
|
expect(response.statusCode).to.equal(404)
|
||||||
return done()
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('when trashing a project', function() {
|
||||||
|
it('should mark the project as trashed for the user', async function() {
|
||||||
|
const { response } = await this.user.doRequest(
|
||||||
|
'POST',
|
||||||
|
`/project/${this.projectId}/trash`
|
||||||
|
)
|
||||||
|
expect(response.statusCode).to.equal(200)
|
||||||
|
|
||||||
|
const trashedProject = await Project.findById(this.projectId).exec()
|
||||||
|
expectObjectIdArrayEqual(trashedProject.trashed, [this.user._id])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does nothing if the user has already trashed the project', async function() {
|
||||||
|
// Mark as trashed the first time
|
||||||
|
await this.user.doRequest('POST', `/project/${this.projectId}/trash`)
|
||||||
|
|
||||||
|
// And then a second time
|
||||||
|
await this.user.doRequest('POST', `/project/${this.projectId}/trash`)
|
||||||
|
|
||||||
|
const trashedProject = await Project.findById(this.projectId).exec()
|
||||||
|
expectObjectIdArrayEqual(trashedProject.trashed, [this.user._id])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when untrashing a project', function() {
|
||||||
|
it('should mark the project as untrashed for the user', async function() {
|
||||||
|
await Project.update(
|
||||||
|
{ _id: this.projectId },
|
||||||
|
{ trashed: [this.user._id] }
|
||||||
|
)
|
||||||
|
const { response } = await this.user.doRequest(
|
||||||
|
'DELETE',
|
||||||
|
`/project/${this.projectId}/trash`
|
||||||
|
)
|
||||||
|
expect(response.statusCode).to.equal(200)
|
||||||
|
|
||||||
|
const trashedProject = await Project.findById(this.projectId).exec()
|
||||||
|
expectObjectIdArrayEqual(trashedProject.trashed, [])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does nothing if the user has already untrashed the project', async function() {
|
||||||
|
await Project.update(
|
||||||
|
{ _id: this.projectId },
|
||||||
|
{ trashed: [this.user._id] }
|
||||||
|
)
|
||||||
|
// Mark as untrashed the first time
|
||||||
|
await this.user.doRequest('DELETE', `/project/${this.projectId}/trash`)
|
||||||
|
|
||||||
|
// And then a second time
|
||||||
|
await this.user.doRequest('DELETE', `/project/${this.projectId}/trash`)
|
||||||
|
|
||||||
|
const trashedProject = await Project.findById(this.projectId).exec()
|
||||||
|
expectObjectIdArrayEqual(trashedProject.trashed, [])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets trashed to an empty array if not set', async function() {
|
||||||
|
await this.user.doRequest('DELETE', `/project/${this.projectId}/trash`)
|
||||||
|
|
||||||
|
const trashedProject = await Project.findById(this.projectId).exec()
|
||||||
|
expectObjectIdArrayEqual(trashedProject.trashed, [])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function expectObjectIdArrayEqual(objectIdArray, stringArray) {
|
||||||
|
const stringifiedArray = objectIdArray.map(id => id.toString())
|
||||||
|
expect(stringifiedArray).to.deep.equal(stringArray)
|
||||||
|
}
|
||||||
|
|
|
@ -171,7 +171,8 @@ describe('ProjectController', function() {
|
||||||
'../Institutions/InstitutionsAPI': {
|
'../Institutions/InstitutionsAPI': {
|
||||||
getUserAffiliations: this.getUserAffiliations
|
getUserAffiliations: this.getUserAffiliations
|
||||||
},
|
},
|
||||||
'../V1/V1Handler': {}
|
'../V1/V1Handler': {},
|
||||||
|
'../../models/Project': {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue