diff --git a/services/docstore/app.js b/services/docstore/app.js index d6c499d157..9be08d046c 100644 --- a/services/docstore/app.js +++ b/services/docstore/app.js @@ -46,6 +46,7 @@ Metrics.injectMetricsRoute(app) app.get('/project/:project_id/doc', HttpController.getAllDocs) app.get('/project/:project_id/ranges', HttpController.getAllRanges) app.get('/project/:project_id/doc/:doc_id', HttpController.getDoc) +app.get('/project/:project_id/doc/:doc_id/deleted', HttpController.isDocDeleted) app.get('/project/:project_id/doc/:doc_id/raw', HttpController.getRawDoc) // Add 64kb overhead for the JSON encoding, and double the size to allow for ranges in the json payload app.post( diff --git a/services/docstore/app/js/DocManager.js b/services/docstore/app/js/DocManager.js index edd9c0f1cd..bbb4f7fecd 100644 --- a/services/docstore/app/js/DocManager.js +++ b/services/docstore/app/js/DocManager.js @@ -90,6 +90,24 @@ module.exports = DocManager = { ) }, + isDocDeleted(projectId, docId, callback) { + MongoManager.findDoc(projectId, docId, { deleted: true }, function ( + err, + doc + ) { + if (err) { + return callback(err) + } + if (!doc) { + return callback( + new Errors.NotFoundError(`No such project/doc: ${projectId}/${docId}`) + ) + } + // `doc.deleted` is `undefined` for non deleted docs + callback(null, Boolean(doc.deleted)) + }) + }, + getFullDoc(project_id, doc_id, callback) { if (callback == null) { callback = function (err, doc) {} diff --git a/services/docstore/app/js/HttpController.js b/services/docstore/app/js/HttpController.js index 320557a5f6..947b83036c 100644 --- a/services/docstore/app/js/HttpController.js +++ b/services/docstore/app/js/HttpController.js @@ -44,6 +44,16 @@ module.exports = HttpController = { }) }, + isDocDeleted(req, res, next) { + const { doc_id: docId, project_id: projectId } = req.params + DocManager.isDocDeleted(projectId, docId, function (error, deleted) { + if (error) { + return next(error) + } + res.json({ deleted }) + }) + }, + getRawDoc(req, res, next) { if (next == null) { next = function (error) {} diff --git a/services/docstore/test/acceptance/js/DeletingDocsTests.js b/services/docstore/test/acceptance/js/DeletingDocsTests.js index 24c78903ee..80a448cdcc 100644 --- a/services/docstore/test/acceptance/js/DeletingDocsTests.js +++ b/services/docstore/test/acceptance/js/DeletingDocsTests.js @@ -44,6 +44,19 @@ describe('Deleting a doc', function () { }) }) + it('should show as not deleted on /deleted', function (done) { + DocstoreClient.isDocDeleted( + this.project_id, + this.doc_id, + (error, res, body) => { + if (error) return done(error) + expect(res.statusCode).to.equal(200) + expect(body).to.have.property('deleted').to.equal(false) + done() + } + ) + }) + describe('when the doc exists', function () { beforeEach(function (done) { return DocstoreClient.deleteDoc( @@ -60,6 +73,19 @@ describe('Deleting a doc', function () { return db.docs.remove({ _id: this.doc_id }, done) }) + it('should mark the doc as deleted on /deleted', function (done) { + DocstoreClient.isDocDeleted( + this.project_id, + this.doc_id, + (error, res, body) => { + if (error) return done(error) + expect(res.statusCode).to.equal(200) + expect(body).to.have.property('deleted').to.equal(true) + done() + } + ) + }) + return it('should insert a deleted doc into the docs collection', function (done) { return db.docs.find({ _id: this.doc_id }).toArray((error, docs) => { docs[0]._id.should.deep.equal(this.doc_id) @@ -70,7 +96,40 @@ describe('Deleting a doc', function () { }) }) + describe('when the doc exists in another project', function () { + const otherProjectId = ObjectId() + + it('should show as not existing on /deleted', function (done) { + DocstoreClient.isDocDeleted(otherProjectId, this.doc_id, (error, res) => { + if (error) return done(error) + expect(res.statusCode).to.equal(404) + done() + }) + }) + + it('should return a 404 when trying to delete', function (done) { + DocstoreClient.deleteDoc(otherProjectId, this.doc_id, (error, res) => { + if (error) return done(error) + expect(res.statusCode).to.equal(404) + done() + }) + }) + }) + return describe('when the doc does not exist', function () { + it('should show as not existing on /deleted', function (done) { + const missing_doc_id = ObjectId() + DocstoreClient.isDocDeleted( + this.project_id, + missing_doc_id, + (error, res) => { + if (error) return done(error) + expect(res.statusCode).to.equal(404) + done() + } + ) + }) + return it('should return a 404', function (done) { const missing_doc_id = ObjectId() return DocstoreClient.deleteDoc( diff --git a/services/docstore/test/acceptance/js/helpers/DocstoreClient.js b/services/docstore/test/acceptance/js/helpers/DocstoreClient.js index 92b2b95e21..ee4f26520e 100644 --- a/services/docstore/test/acceptance/js/helpers/DocstoreClient.js +++ b/services/docstore/test/acceptance/js/helpers/DocstoreClient.js @@ -60,6 +60,16 @@ module.exports = DocstoreClient = { ) }, + isDocDeleted(project_id, doc_id, callback) { + request.get( + { + url: `http://localhost:${settings.internal.docstore.port}/project/${project_id}/doc/${doc_id}/deleted`, + json: true + }, + callback + ) + }, + getAllDocs(project_id, callback) { if (callback == null) { callback = function (error, res, body) {}