Socket
Socket
Sign inDemoInstall

@cloudant/couchbackup

Package Overview
Dependencies
Maintainers
3
Versions
479
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cloudant/couchbackup - npm Package Compare versions

Comparing version 2.3.0-SNAPSHOT.127 to 2.3.0-SNAPSHOT.128

255

app.js

@@ -22,9 +22,10 @@ // Copyright © 2017, 2018 IBM Corp. All rights reserved.

const backupFull = require('./includes/backup.js');
const defaults = require('./includes/config.js').apiDefaults();
const error = require('./includes/error.js');
const request = require('./includes/request.js');
const restoreInternal = require('./includes/restore.js');
const backupShallow = require('./includes/shallowbackup.js');
const backupFull = require('./includes/backup.js');
const defaults = require('./includes/config.js').apiDefaults();
const debug = require('debug')('couchbackup:app');
const events = require('events');
const debug = require('debug')('couchbackup:app');
const error = require('./includes/error.js');
const fs = require('fs');

@@ -140,2 +141,29 @@ const legacyUrl = require('url');

/*
Check the referenced database exists and that the credentials used have
visibility. Callback with a fatal error if there is a problem with the DB.
@param {string} db - database object
@param {function(err)} callback - error is undefined if DB exists
*/
function proceedIfDbValid(db, callback) {
db.head('', function(err) {
err = error.convertResponseError(err, function(err) {
if (err && err.statusCode === 404) {
// Override the error type and mesasge for the DB not found case
var msg = `Database ${db.config.url.replace(/\/\/.+@/g, '//****:****@')}` +
`/${db.config.db} does not exist. ` +
'Check the URL and database name have been specified correctly.';
var noDBErr = new Error(msg);
noDBErr.name = 'DatabaseNotFound';
return noDBErr;
} else {
// Delegate to the default error factory if it wasn't a 404
return error.convertResponseError(err);
}
});
// Callback with or without (i.e. undefined) error
callback(err);
});
}
module.exports = {

@@ -167,13 +195,3 @@

}
opts = Object.assign({}, defaults, opts);
var backup = null;
if (opts.mode === 'shallow') {
backup = backupShallow;
} else { // full mode
backup = backupFull;
}
const ee = new events.EventEmitter();
// if there is an error writing to the stream, call the completion

@@ -186,64 +204,88 @@ // callback with the error set

// If resuming write a newline as it's possible one would be missing from
// an interruption of the previous backup. If the backup was clean this
// will cause an empty line that will be gracefully handled by the restore.
if (opts.resume) {
targetStream.write('\n');
}
opts = Object.assign({}, defaults, opts);
// Get the event emitter from the backup process so we can handle events
// before passing them on to the app's event emitter if needed.
const internalEE = backup(srcUrl, opts);
addEventListener(listenerErrorIndicator, internalEE, 'changes', function(batch) {
ee.emit('changes', batch);
});
addEventListener(listenerErrorIndicator, internalEE, 'received', function(obj, q, logCompletedBatch) {
// this may be too verbose to have as well as the "backed up" message
// debug(' received batch', obj.batch, ' docs: ', obj.total, 'Time', obj.time);
// Callback to emit the written event when the content is flushed
function writeFlushed() {
ee.emit('written', {total: obj.total, time: obj.time, batch: obj.batch});
if (logCompletedBatch) {
logCompletedBatch(obj.batch);
const ee = new events.EventEmitter();
// Set up the DB client
const backupDB = request.client(srcUrl, opts);
// Validate the DB exists, before proceeding to backup
proceedIfDbValid(backupDB, function(err) {
if (err) {
if (err.name === 'DatabaseNotFound') {
err.message = `${err.message} Ensure the backup source database exists.`;
}
debug(' backed up batch', obj.batch, ' docs: ', obj.total, 'Time', obj.time);
// Didn't exist, or another fatal error, exit
callback(err);
return;
}
// Write the received content to the targetStream
const continueWriting = targetStream.write(JSON.stringify(obj.data) + '\n',
'utf8',
writeFlushed);
if (!continueWriting) {
// The buffer was full, pause the queue to stop the writes until we
// get a drain event
if (q && !q.isPaused) {
q.pause();
targetStream.once('drain', function() {
q.resume();
});
}
var backup = null;
if (opts.mode === 'shallow') {
backup = backupShallow;
} else { // full mode
backup = backupFull;
}
});
// For errors we expect, may or may not be fatal
addEventListener(listenerErrorIndicator, internalEE, 'error', function(err) {
debug('Error ' + JSON.stringify(err));
callback(err);
});
addEventListener(listenerErrorIndicator, internalEE, 'finished', function(obj) {
function emitFinished() {
debug('Backup complete - written ' + JSON.stringify(obj));
const summary = {total: obj.total};
ee.emit('finished', summary);
if (callback) callback(null, summary);
// If resuming write a newline as it's possible one would be missing from
// an interruption of the previous backup. If the backup was clean this
// will cause an empty line that will be gracefully handled by the restore.
if (opts.resume) {
targetStream.write('\n');
}
if (targetStream === process.stdout) {
// stdout cannot emit a finish event so use a final write + callback
targetStream.write('', 'utf8', emitFinished);
} else {
// If we're writing to a file, end the writes and register the
// emitFinished function for a callback when the file stream's finish
// event is emitted.
targetStream.end('', 'utf8', emitFinished);
}
// Get the event emitter from the backup process so we can handle events
// before passing them on to the app's event emitter if needed.
const internalEE = backup(backupDB, opts);
addEventListener(listenerErrorIndicator, internalEE, 'changes', function(batch) {
ee.emit('changes', batch);
});
addEventListener(listenerErrorIndicator, internalEE, 'received', function(obj, q, logCompletedBatch) {
// this may be too verbose to have as well as the "backed up" message
// debug(' received batch', obj.batch, ' docs: ', obj.total, 'Time', obj.time);
// Callback to emit the written event when the content is flushed
function writeFlushed() {
ee.emit('written', {total: obj.total, time: obj.time, batch: obj.batch});
if (logCompletedBatch) {
logCompletedBatch(obj.batch);
}
debug(' backed up batch', obj.batch, ' docs: ', obj.total, 'Time', obj.time);
}
// Write the received content to the targetStream
const continueWriting = targetStream.write(JSON.stringify(obj.data) + '\n',
'utf8',
writeFlushed);
if (!continueWriting) {
// The buffer was full, pause the queue to stop the writes until we
// get a drain event
if (q && !q.isPaused) {
q.pause();
targetStream.once('drain', function() {
q.resume();
});
}
}
});
// For errors we expect, may or may not be fatal
addEventListener(listenerErrorIndicator, internalEE, 'error', function(err) {
debug('Error ' + JSON.stringify(err));
callback(err);
});
addEventListener(listenerErrorIndicator, internalEE, 'finished', function(obj) {
function emitFinished() {
debug('Backup complete - written ' + JSON.stringify(obj));
const summary = {total: obj.total};
ee.emit('finished', summary);
if (callback) callback(null, summary);
}
if (targetStream === process.stdout) {
// stdout cannot emit a finish event so use a final write + callback
targetStream.write('', 'utf8', emitFinished);
} else {
// If we're writing to a file, end the writes and register the
// emitFinished function for a callback when the file stream's finish
// event is emitted.
targetStream.end('', 'utf8', emitFinished);
}
});
});
return ee;

@@ -274,37 +316,50 @@ },

restoreInternal(
targetUrl,
opts,
srcStream,
ee,
function(err, writer) {
if (err) {
callback(err, null);
return;
// Set up the DB client
const restoreDB = request.client(targetUrl, opts);
// Validate the DB exists, before proceeding to restore
proceedIfDbValid(restoreDB, function(err) {
if (err) {
if (err.name === 'DatabaseNotFound') {
err.message = `${err.message} Create the target database before restoring.`;
}
if (writer != null) {
addEventListener(listenerErrorIndicator, writer, 'restored', function(obj) {
debug(' restored ', obj.total);
ee.emit('restored', {documents: obj.documents, total: obj.total});
});
addEventListener(listenerErrorIndicator, writer, 'error', function(err) {
debug('Error ' + JSON.stringify(err));
// Only call destroy if it is available on the stream
if (srcStream.destroy && srcStream.destroy instanceof Function) {
srcStream.destroy();
}
callback(err);
});
addEventListener(listenerErrorIndicator, writer, 'finished', function(obj) {
debug('restore complete');
ee.emit('finished', {total: obj.total});
callback(null, obj);
});
}
// Didn't exist, or another fatal error, exit
callback(err);
return;
}
);
restoreInternal(
restoreDB,
opts,
srcStream,
ee,
function(err, writer) {
if (err) {
callback(err, null);
return;
}
if (writer != null) {
addEventListener(listenerErrorIndicator, writer, 'restored', function(obj) {
debug(' restored ', obj.total);
ee.emit('restored', {documents: obj.documents, total: obj.total});
});
addEventListener(listenerErrorIndicator, writer, 'error', function(err) {
debug('Error ' + JSON.stringify(err));
// Only call destroy if it is available on the stream
if (srcStream.destroy && srcStream.destroy instanceof Function) {
srcStream.destroy();
}
callback(err);
});
addEventListener(listenerErrorIndicator, writer, 'finished', function(obj) {
debug('restore complete');
ee.emit('finished', {total: obj.total});
callback(null, obj);
});
}
}
);
});
return ee;
}
};

@@ -311,0 +366,0 @@

# 2.3.0 (UNRELEASED)
- [NEW] Check for database existence before starting backup. This provides for
better error messages for existence, authentication, and `_bulk_get` problems.
- [FIXED] Intermittent issues with multiple callbacks, particularly noticeable

@@ -6,3 +9,3 @@ when using Node.js 10.

fatal error.
- [UPGRADED] Increased nodejs-cloudant dependency minimum to 2.2.x.
- [UPGRADED] Increased nodejs-cloudant dependency minimum to 2.2.x.

@@ -9,0 +12,0 @@ # 2.2.0 (2018-03-06)

@@ -18,3 +18,2 @@ // Copyright © 2017, 2018 IBM Corp. All rights reserved.

const events = require('events');
const request = require('./request.js');
const fs = require('fs');

@@ -29,3 +28,3 @@ const error = require('./error.js');

*
* @param {string} dbUrl - URL of source database.
* @param {string} db - `@cloudant/cloudant` DB object for source database.
* @param {number} blocksize - number of documents to download in single request

@@ -40,3 +39,3 @@ * @param {number} parallelism - number of concurrent downloads

*/
module.exports = function(dbUrl, options) {
module.exports = function(db, options) {
const ee = new events.EventEmitter();

@@ -46,4 +45,2 @@ const start = new Date().getTime(); // backup start time

const db = request.client(dbUrl, options);
function proceedWithBackup() {

@@ -50,0 +47,0 @@ if (options.resume) {

@@ -20,3 +20,3 @@ // Copyright © 2017, 2018 IBM Corp. All rights reserved.

'InvalidOption': 2,
'RestoreDatabaseNotFound': 10,
'DatabaseNotFound': 10,
'Unauthorized': 11,

@@ -23,0 +23,0 @@ 'Forbidden': 12,

@@ -16,53 +16,17 @@ // Copyright © 2017, 2018 IBM Corp. All rights reserved.

const request = require('./request.js');
const error = require('./error.js');
module.exports = function(db, options, readstream, ee, callback) {
var liner = require('../includes/liner.js')();
var writer = require('../includes/writer.js')(db, options.bufferSize, options.parallelism, ee);
module.exports = function(dbUrl, options, readstream, ee, callback) {
var db = request.client(dbUrl, options);
// pipe the input to the output, via transformation functions
readstream
.pipe(liner) // transform the input stream into per-line
.on('error', function(err) {
// Forward the error to the writer event emitter where we already have
// listeners on for handling errors
writer.emit('error', err);
})
.pipe(writer); // transform the data
exists(db, function(err) {
if (err) {
callback(err);
return;
}
var liner = require('../includes/liner.js')();
var writer = require('../includes/writer.js')(db, options.bufferSize, options.parallelism, ee);
// pipe the input to the output, via transformation functions
readstream
.pipe(liner) // transform the input stream into per-line
.on('error', function(err) {
// Forward the error to the writer event emitter where we already have
// listeners on for handling errors
writer.emit('error', err);
})
.pipe(writer); // transform the data
callback(null, writer);
});
callback(null, writer);
};
/*
Check couchDbUrl is a valid database URL.
@param {string} couchDbUrl - Database URL
@param {function(err, exists)} callback - exists is true if database exists
*/
function exists(db, callback) {
db.head('', function(err) {
err = error.convertResponseError(err, function(err) {
if (err && err.statusCode === 404) {
// Override the error type and mesasge for the DB not found case
var noDBErr = new Error(`Database ${db.config.url}/${db.config.db} does not exist. ` +
'Create the target database before restoring.');
noDBErr.name = 'RestoreDatabaseNotFound';
return noDBErr;
} else {
// Delegate to the default error factory if it wasn't a 404
return error.convertResponseError(err);
}
});
// Callback with or without (i.e. undefined) error
callback(err);
});
}

@@ -19,7 +19,4 @@ // Copyright © 2017, 2018 IBM Corp. All rights reserved.

const events = require('events');
const request = require('./request.js');
module.exports = function(dbUrl, options) {
var db = request.client(dbUrl, options);
module.exports = function(db, options) {
const ee = new events.EventEmitter();

@@ -26,0 +23,0 @@ const start = new Date().getTime();

{
"name": "@cloudant/couchbackup",
"version": "2.3.0-SNAPSHOT.127",
"version": "2.3.0-SNAPSHOT.128",
"description": "CouchBackup - command-line backup utility for Cloudant/CouchDB",

@@ -5,0 +5,0 @@ "homepage": "https://github.com/cloudant/couchbackup",

@@ -382,2 +382,3 @@ # CouchBackup

* `2`: invalid CLI option.
* `10`: backup source or restore target database does not exist.
* `11`: unauthorized credentials for the database.

@@ -397,4 +398,2 @@ * `12`: incorrect permissions for the database.

* `10`: restore target database does not exist.
## Note on attachments

@@ -401,0 +400,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc