diff --git a/services/web/app/src/Features/Notifications/NotificationsBuilder.js b/services/web/app/src/Features/Notifications/NotificationsBuilder.js
index 9e884bbcf9..cde8c09a4f 100644
--- a/services/web/app/src/Features/Notifications/NotificationsBuilder.js
+++ b/services/web/app/src/Features/Notifications/NotificationsBuilder.js
@@ -154,5 +154,34 @@ module.exports = {
return NotificationsHandler.markAsReadWithKey(userId, key, callback)
}
}
+ },
+
+ tpdsFileLimit(user_id) {
+ return {
+ key: `tpdsFileLimit-${user_id}`,
+ create(projectName, callback) {
+ if (callback == null) {
+ callback = function() {}
+ }
+ const messageOpts = {
+ projectName: projectName
+ }
+ return NotificationsHandler.createNotification(
+ user_id,
+ this.key,
+ 'notification_tpds_file_limit',
+ messageOpts,
+ null,
+ true,
+ callback
+ )
+ },
+ read(callback) {
+ if (callback == null) {
+ callback = function() {}
+ }
+ return NotificationsHandler.markAsReadByKeyOnly(this.key, callback)
+ }
+ }
}
}
diff --git a/services/web/app/src/Features/ThirdPartyDataStore/TpdsController.js b/services/web/app/src/Features/ThirdPartyDataStore/TpdsController.js
index 03f9eec5d3..ab8b4bd9df 100644
--- a/services/web/app/src/Features/ThirdPartyDataStore/TpdsController.js
+++ b/services/web/app/src/Features/ThirdPartyDataStore/TpdsController.js
@@ -1,8 +1,8 @@
/* eslint-disable
- camelcase,
- handle-callback-err,
- max-len,
-*/
+ camelcase,
+ handle-callback-err,
+ max-len,
+ */
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
@@ -17,6 +17,7 @@ const UpdateMerger = require('./UpdateMerger')
const logger = require('logger-sharelatex')
const Path = require('path')
const metrics = require('metrics-sharelatex')
+const NotificationsBuilder = require('../Notifications/NotificationsBuilder')
module.exports = {
// mergeUpdate and deleteUpdate are used by Dropbox, where the project is only passed as the name, as the
@@ -41,6 +42,13 @@ module.exports = {
'tpds update failed to be processed, too many requests'
)
return res.sendStatus(429)
+ } else if (err.message === 'project_has_too_many_files') {
+ logger.warn(
+ { err, user_id, filePath },
+ 'tpds trying to append to project over file limit'
+ )
+ NotificationsBuilder.tpdsFileLimit(user_id).create(projectName)
+ return res.sendStatus(400)
} else {
logger.err(
{ err, user_id, filePath },
diff --git a/services/web/app/views/project/list/notifications.pug b/services/web/app/views/project/list/notifications.pug
index be1763dd3e..19446e602a 100644
--- a/services/web/app/views/project/list/notifications.pug
+++ b/services/web/app/views/project/list/notifications.pug
@@ -39,10 +39,10 @@
ng-switch-when="notification_ip_matched_affiliation"
)
.notification-body
- | It looks like you're at
+ | It looks like you're at
strong {{ notification.messageOpts.university_name }}!
| Did you know that {{notification.messageOpts.university_name}} is providing
- strong free Overleaf Professional accounts
+ strong free Overleaf Professional accounts
| to everyone at {{notification.messageOpts.university_name}}?
| Add an institutional email address to claim your account.
.notification-action
@@ -53,6 +53,22 @@
span(aria-hidden="true") ×
span.sr-only #{translate("close")}
+ .alert.alert-danger(
+ ng-switch-when="notification_tpds_file_limit"
+ )
+ .notification-body
+ | Error: Your project
+ strong {{ notification.messageOpts.projectName }}
+ | has gone over the 2000 file limit using an integration (e.g. Dropbox or Github)
+ | Please decrease the size of your project to prevent further errors.
+ .notification-action
+ a.pull-right.btn.btn-sm.btn-info(href="/user/settings")
+ | Account Settings
+ .notification-close
+ button.btn-sm(ng-click="dismiss(notification)").close.pull-right
+ span(aria-hidden="true") ×
+ span.sr-only #{translate("close")}
+
.alert.alert-info(
ng-switch-default
)
@@ -98,7 +114,7 @@
ng-switch-when="notification_institution_sso_non_canonical"
)
.notification-body
- div
+ div
i.fa.fa-fw.fa-exclamation-triangle(aria-hidden="true")
| !{translate("tried_to_log_in_with_email", {email: "{{notification.requestedEmail}}"})} !{translate("in_order_to_match_institutional_metadata_associated", {email: "{{notification.institutionEmail}}"})}
.notification-close
@@ -124,7 +140,7 @@
ng-switch-when="notification_institution_sso_linked_by_another"
)
.notification-body
- div
+ div
i.fa.fa-fw.fa-exclamation-triangle(aria-hidden="true")
| !{translate("institution_account_tried_to_add_already_registered")}
.notification-close
@@ -152,4 +168,4 @@
.notification-body
i.fa.fa-spinner.fa-spin(aria-hidden="true")
|
- | #{translate('resending_confirmation_email')}…
\ No newline at end of file
+ | #{translate('resending_confirmation_email')}…
diff --git a/services/web/test/unit/src/ThirdPartyDataStore/TpdsControllerTests.js b/services/web/test/unit/src/ThirdPartyDataStore/TpdsControllerTests.js
index bee16feede..4adbf25104 100644
--- a/services/web/test/unit/src/ThirdPartyDataStore/TpdsControllerTests.js
+++ b/services/web/test/unit/src/ThirdPartyDataStore/TpdsControllerTests.js
@@ -28,6 +28,9 @@ describe('TpdsController', function() {
requires: {
'./TpdsUpdateHandler': this.TpdsUpdateHandler,
'./UpdateMerger': (this.UpdateMerger = {}),
+ '../Notifications/NotificationsBuilder': (this.NotificationsBuilder = {
+ tpdsFileLimit: sinon.stub().returns({ create: sinon.stub() })
+ }),
'logger-sharelatex': {
log() {},
warn() {},
@@ -95,6 +98,31 @@ describe('TpdsController', function() {
res.sendStatus.calledWith(500).should.equal(true)
})
+ it('should return a 400 error when the project is too big', function() {
+ const path = '/projectName/here.txt'
+ const req = {
+ pause() {},
+ params: { 0: path, user_id: this.user_id, projectName: 'projectName' },
+ session: {
+ destroy() {}
+ },
+ headers: {
+ 'x-sl-update-source': (this.source = 'dropbox')
+ }
+ }
+ this.TpdsUpdateHandler.newUpdate = sinon
+ .stub()
+ .callsArgWith(5, { message: 'project_has_too_many_files' })
+ const res = {
+ sendStatus: sinon.stub()
+ }
+ this.TpdsController.mergeUpdate(req, res)
+ res.sendStatus.calledWith(400).should.equal(true)
+ this.NotificationsBuilder.tpdsFileLimit
+ .calledWith(this.user_id)
+ .should.equal(true)
+ })
+
it('should return a 429 error when the update receiver fails due to too many requests error', function() {
const path = '/projectName/here.txt'
const req = {