diff --git a/services/document-updater/app.js b/services/document-updater/app.js index c489abf0de..e5a1cda19b 100644 --- a/services/document-updater/app.js +++ b/services/document-updater/app.js @@ -185,6 +185,8 @@ app.use((error, req, res, next) => { return res.sendStatus(404) } else if (error instanceof Errors.OpRangeNotAvailableError) { return res.sendStatus(422) // Unprocessable Entity + } else if (error instanceof Errors.FileTooLargeError) { + return res.sendStatus(413) } else if (error.statusCode === 413) { return res.status(413).send('request entity too large') } else { diff --git a/services/document-updater/app/js/Errors.js b/services/document-updater/app/js/Errors.js index f402343b14..c441af4db3 100644 --- a/services/document-updater/app/js/Errors.js +++ b/services/document-updater/app/js/Errors.js @@ -37,9 +37,18 @@ function DeleteMismatchError(message) { } DeleteMismatchError.prototype.__proto__ = Error.prototype +function FileTooLargeError(message) { + const error = new Error(message) + error.name = 'FileTooLargeError' + error.__proto__ = FileTooLargeError.prototype + return error +} +FileTooLargeError.prototype.__proto__ = Error.prototype + module.exports = Errors = { NotFoundError, OpRangeNotAvailableError, ProjectStateChangedError, DeleteMismatchError, + FileTooLargeError, } diff --git a/services/document-updater/app/js/PersistenceManager.js b/services/document-updater/app/js/PersistenceManager.js index 6d4e5f6424..88314935bb 100644 --- a/services/document-updater/app/js/PersistenceManager.js +++ b/services/document-updater/app/js/PersistenceManager.js @@ -104,6 +104,10 @@ function getDoc(projectId, docId, options = {}, _callback) { ) } else if (res.statusCode === 404) { callback(new Errors.NotFoundError(`doc not not found: ${urlPath}`)) + } else if (res.statusCode === 413) { + callback( + new Errors.FileTooLargeError(`doc exceeds maximum size: ${urlPath}`) + ) } else { callback( new Error(`error accessing web API: ${urlPath} ${res.statusCode}`) @@ -158,6 +162,10 @@ function setDoc( callback(null) } else if (res.statusCode === 404) { callback(new Errors.NotFoundError(`doc not not found: ${urlPath}`)) + } else if (res.statusCode === 413) { + callback( + new Errors.FileTooLargeError(`doc exceeds maximum size: ${urlPath}`) + ) } else { callback( new Error(`error accessing web API: ${urlPath} ${res.statusCode}`) diff --git a/services/document-updater/test/unit/js/PersistenceManager/PersistenceManagerTests.js b/services/document-updater/test/unit/js/PersistenceManager/PersistenceManagerTests.js index aa25a22345..2d0c622fec 100644 --- a/services/document-updater/test/unit/js/PersistenceManager/PersistenceManagerTests.js +++ b/services/document-updater/test/unit/js/PersistenceManager/PersistenceManagerTests.js @@ -206,6 +206,33 @@ describe('PersistenceManager', function () { }) }) + describe('when the request returns 413', function () { + beforeEach(function () { + this.request.callsArgWith(1, null, { statusCode: 413 }, '') + this.PersistenceManager.getDoc( + this.project_id, + this.doc_id, + this.callback + ) + }) + + it('should return a FileTooLargeError', function () { + this.callback + .calledWith(sinon.match.instanceOf(Errors.FileTooLargeError)) + .should.equal(true) + }) + + it('should time the execution', function () { + this.Metrics.Timer.prototype.done.called.should.equal(true) + }) + + it('should increment the metric', function () { + this.Metrics.inc + .calledWith('getDoc', 1, { status: 413 }) + .should.equal(true) + }) + }) + describe('when the request returns an error status code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '') @@ -427,6 +454,38 @@ describe('PersistenceManager', function () { }) }) + describe('when the request returns 413', function () { + beforeEach(function () { + this.request.callsArgWith(1, null, { statusCode: 413 }, '') + this.PersistenceManager.setDoc( + this.project_id, + this.doc_id, + this.lines, + this.version, + this.ranges, + this.lastUpdatedAt, + this.lastUpdatedBy, + this.callback + ) + }) + + it('should return a FileTooLargeError', function () { + this.callback + .calledWith(sinon.match.instanceOf(Errors.FileTooLargeError)) + .should.equal(true) + }) + + it('should time the execution', function () { + this.Metrics.Timer.prototype.done.called.should.equal(true) + }) + + it('should increment the metric', function () { + this.Metrics.inc + .calledWith('setDoc', 1, { status: 413 }) + .should.equal(true) + }) + }) + describe('when the request returns an error status code', function () { beforeEach(function () { this.request.callsArgWith(1, null, { statusCode: 500 }, '')