diff --git a/src/backups.js b/src/backups.js index 86811e09e..1b2fb6225 100644 --- a/src/backups.js +++ b/src/backups.js @@ -6,6 +6,7 @@ exports = module.exports = { getByTypePaged, add, update, + setState, list, del, @@ -129,13 +130,14 @@ async function add(data) { assert(Array.isArray(data.dependsOn)); assert.strictEqual(typeof data.manifest, 'object'); assert.strictEqual(typeof data.format, 'string'); + assert.strictEqual(typeof data.preserveSecs, 'number'); const creationTime = data.creationTime || new Date(); // allow tests to set the time const manifestJson = JSON.stringify(data.manifest); const id = 'bid-' + uuid.v4(); - const [error] = await safe(database.query('INSERT INTO backups (id, remotePath, identifier, encryptionVersion, packageVersion, type, creationTime, state, dependsOnJson, manifestJson, format) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', - [ id, data.remotePath, data.identifier, data.encryptionVersion, data.packageVersion, data.type, creationTime, data.state, JSON.stringify(data.dependsOn), manifestJson, data.format ])); + const [error] = await safe(database.query('INSERT INTO backups (id, remotePath, identifier, encryptionVersion, packageVersion, type, creationTime, state, dependsOnJson, manifestJson, format, preserveSecs) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + [ id, data.remotePath, data.identifier, data.encryptionVersion, data.packageVersion, data.type, creationTime, data.state, JSON.stringify(data.dependsOn), manifestJson, data.format, data.preserveSecs ])); if (error && error.code === 'ER_DUP_ENTRY') throw new BoxError(BoxError.ALREADY_EXISTS, 'Backup already exists'); if (error) throw error; @@ -186,6 +188,7 @@ function validateLabel(label) { return null; } +// this is called by REST API async function update(id, data) { assert.strictEqual(typeof id, 'string'); assert.strictEqual(typeof data, 'object'); @@ -198,15 +201,33 @@ async function update(id, data) { const fields = [], values = []; for (const p in data) { - if (p === 'label' || p === 'preserveSecs' || p === 'state') { + if (p === 'label' || p === 'preserveSecs') { fields.push(p + ' = ?'); values.push(data[p]); } } values.push(id); + const backup = await get(id); + if (backup === null) throw new BoxError(BoxError.NOT_FOUND, 'Backup not found'); + const result = await database.query('UPDATE backups SET ' + fields.join(', ') + ' WHERE id = ?', values); if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'Backup not found'); + + if ('preserveSecs' in data) { + // update the dependancies + for (const depId of backup.dependsOn) { + await database.query('UPDATE backups SET preserveSecs=? WHERE id = ?', [ data.preserveSecs, depId]); + } + } +} + +async function setState(id, state) { + assert.strictEqual(typeof id, 'string'); + assert.strictEqual(typeof state, 'string'); + + const result = await database.query('UPDATE backups SET state = ? WHERE id = ?', [state, id]); + if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'Backup not found'); } async function startBackupTask(auditSource) { diff --git a/src/backuptask.js b/src/backuptask.js index cd1fffb12..3af737f6b 100644 --- a/src/backuptask.js +++ b/src/backuptask.js @@ -797,13 +797,14 @@ async function rotateBoxBackup(backupConfig, tag, options, dependsOn, progressCa identifier: backups.BACKUP_IDENTIFIER_BOX, dependsOn, manifest: null, - format + format, + preserveSecs: options.preserveSecs || 0 }; const id = await backups.add(data); const [copyBackupError] = await safe(copy(backupConfig, 'snapshot/box', remotePath, progressCallback)); const state = copyBackupError ? backups.BACKUP_STATE_ERROR : backups.BACKUP_STATE_NORMAL; - await backups.update(id, { preserveSecs: options.preserveSecs || 0, state }); + await backups.setState(id, state); if (copyBackupError) throw copyBackupError; return id; @@ -843,15 +844,16 @@ async function rotateAppBackup(backupConfig, app, tag, options, progressCallback type: backups.BACKUP_TYPE_APP, state: backups.BACKUP_STATE_CREATING, identifier: app.id, - dependsOn: [ ], + dependsOn: [], manifest, - format: format + format, + preserveSecs: options.preserveSecs || 0 }; const id = await backups.add(data); const copyBackupError = await safe(copy(backupConfig, `snapshot/app_${app.id}`, remotePath, progressCallback)); const state = copyBackupError ? backups.BACKUP_STATE_ERROR : backups.BACKUP_STATE_NORMAL; - await backups.update(id, { preserveSecs: options.preserveSecs || 0, state }); + await backups.setState(id, state); if (copyBackupError) throw copyBackupError; return id; @@ -979,13 +981,14 @@ async function rotateMailBackup(backupConfig, tag, options, progressCallback) { identifier: backups.BACKUP_IDENTIFIER_MAIL, dependsOn: [], manifest: null, - format: format + format, + preserveSecs: options.preserveSecs || 0 }; const id = await backups.add(data); const [copyBackupError] = await safe(copy(backupConfig, 'snapshot/mail', remotePath, progressCallback)); const state = copyBackupError ? backups.BACKUP_STATE_ERROR : backups.BACKUP_STATE_NORMAL; - await backups.update(id, { preserveSecs: options.preserveSecs || 0, state }); + await backups.setState(id, state); if (copyBackupError) throw copyBackupError; return id; diff --git a/src/test/backupcleaner-test.js b/src/test/backupcleaner-test.js index 611e36bc5..d24dd0904 100644 --- a/src/test/backupcleaner-test.js +++ b/src/test/backupcleaner-test.js @@ -135,7 +135,8 @@ describe('backup cleaner', function () { state: backups.BACKUP_STATE_NORMAL, dependsOn: [ 'backup-app-00', 'backup-app-01' ], manifest: null, - format: 'tgz' + format: 'tgz', + preserveSecs: 0 }; const BACKUP_0_APP_0 = { // backup of installed app @@ -148,7 +149,8 @@ describe('backup cleaner', function () { state: backups.BACKUP_STATE_NORMAL, dependsOn: [], manifest: null, - format: 'tgz' + format: 'tgz', + preserveSecs: 0 }; const BACKUP_0_APP_1 = { // this app is uninstalled @@ -161,7 +163,8 @@ describe('backup cleaner', function () { state: backups.BACKUP_STATE_NORMAL, dependsOn: [], manifest: null, - format: 'tgz' + format: 'tgz', + preserveSecs: 0 }; const BACKUP_1_BOX = { @@ -174,7 +177,8 @@ describe('backup cleaner', function () { identifier: 'box', dependsOn: [ 'backup-app-10', 'backup-app-11' ], manifest: null, - format: 'tgz' + format: 'tgz', + preserveSecs: 0 }; const BACKUP_1_APP_0 = { @@ -187,7 +191,8 @@ describe('backup cleaner', function () { identifier: app.id, dependsOn: [], manifest: null, - format: 'tgz' + format: 'tgz', + preserveSecs: 0 }; const BACKUP_1_APP_1 = { @@ -200,7 +205,8 @@ describe('backup cleaner', function () { identifier: 'app1', dependsOn: [], manifest: null, - format: 'tgz' + format: 'tgz', + preserveSecs: 0 }; before(async function () {