You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
5.5 KiB
207 lines
5.5 KiB
/** |
|
* Copyright (c) 2013 Yahoo! Inc. All rights reserved. |
|
* |
|
* Copyrights licensed under the MIT License. See the accompanying LICENSE file |
|
* for terms. |
|
*/ |
|
|
|
|
|
var assert = require('assert'); |
|
var jute = require('./jute'); |
|
var Path = require('./Path.js'); |
|
var ACL = require('./ACL.js'); |
|
var Exception = require('./Exception.js'); |
|
var CreateMode = require('./CreateMode.js'); |
|
var ConnectionManager = require('./ConnectionManager.js'); |
|
|
|
/** |
|
* Transaction provides a builder interface that helps building an atomic set |
|
* of operations. |
|
* |
|
* @class Transaction |
|
* @constructor |
|
* @param connectionManager {ConnectionManager} an instance of ConnectionManager. |
|
*/ |
|
function Transaction(connectionManager) { |
|
if (!(this instanceof Transaction)) { |
|
return new Transaction(connectionManager); |
|
} |
|
|
|
assert( |
|
connectionManager instanceof ConnectionManager, |
|
'connectionManager must be an instance of ConnectionManager.' |
|
); |
|
|
|
this.ops = []; |
|
this.connectionManager = connectionManager; |
|
} |
|
|
|
/** |
|
* Add a create operation with given path, data, acls and mode. |
|
* |
|
* @method create |
|
* @param path {String} The znode path. |
|
* @param [data=undefined] {Buffer} The data buffer. |
|
* @param [acls=ACL.OPEN_ACL_UNSAFE] {Array} An array of ACL object. |
|
* @param [mode=CreateMode.PERSISTENT] {CreateMode} The creation mode. |
|
* @return {Transaction} this transaction instance. |
|
*/ |
|
Transaction.prototype.create = function (path, data, acls, mode) { |
|
var optionalArgs = [data, acls, mode], |
|
self = this, |
|
currentPath = '', |
|
nodes; |
|
|
|
Path.validate(path); |
|
|
|
// Reset arguments so we can reassign correct value to them. |
|
data = acls = mode = undefined; |
|
optionalArgs.forEach(function (arg, index) { |
|
if (Array.isArray(arg)) { |
|
acls = arg; |
|
} else if (typeof arg === 'number') { |
|
mode = arg; |
|
} else if (Buffer.isBuffer(arg)) { |
|
data = arg; |
|
} |
|
}); |
|
|
|
acls = Array.isArray(acls) ? acls : ACL.OPEN_ACL_UNSAFE; |
|
mode = typeof mode === 'number' ? mode : CreateMode.PERSISTENT; |
|
|
|
assert( |
|
data === null || data === undefined || Buffer.isBuffer(data), |
|
'data must be a valid buffer, null or undefined.' |
|
); |
|
|
|
assert(acls.length > 0, 'acls must be a non-empty array.'); |
|
|
|
this.ops.push({ |
|
type : jute.OP_CODES.CREATE, |
|
path : path, |
|
data : data, |
|
acls : acls, |
|
mode : mode |
|
}); |
|
|
|
return this; |
|
}; |
|
|
|
/** |
|
* Add a check (existence) operation with given path and optional version. |
|
* |
|
* @method check |
|
* @param path {String} The znode path. |
|
* @param [version=-1] {Number} The version of the znode. |
|
* @return {Transaction} this transaction instance. |
|
*/ |
|
Transaction.prototype.check = function (path, version) { |
|
version = version || -1; |
|
|
|
Path.validate(path); |
|
assert(typeof version === 'number', 'version must be a number.'); |
|
|
|
this.ops.push({ |
|
type : jute.OP_CODES.CHECK, |
|
path : path, |
|
version : version |
|
}); |
|
|
|
return this; |
|
}; |
|
|
|
/** |
|
* Add a set-data operation with the given path, data and optional version. |
|
* |
|
* @method setData |
|
* @param path {String} The znode path. |
|
* @param data {Buffer} The data buffer. |
|
* @param [version=-1] {Number} The version of the znode. |
|
* @return {Transaction} this transaction instance. |
|
*/ |
|
Transaction.prototype.setData = function (path, data, version) { |
|
version = version || -1; |
|
|
|
Path.validate(path); |
|
assert( |
|
data === null || data === undefined || Buffer.isBuffer(data), |
|
'data must be a valid buffer, null or undefined.' |
|
); |
|
assert(typeof version === 'number', 'version must be a number.'); |
|
|
|
this.ops.push({ |
|
type : jute.OP_CODES.SET_DATA, |
|
path : path, |
|
data : data, |
|
version : version |
|
}); |
|
|
|
return this; |
|
}; |
|
|
|
/** |
|
* Add a delete operation with the given path and optional version. |
|
* |
|
* @method delete |
|
* @param path {String} The znode path. |
|
* @param [version=-1] {Number} The version of the znode. |
|
* @return {Transaction} this transaction instance. |
|
*/ |
|
Transaction.prototype.remove = function (path, version) { |
|
version = version || -1; |
|
|
|
Path.validate(path); |
|
assert(typeof version === 'number', 'version must be a number.'); |
|
|
|
this.ops.push({ |
|
type : jute.OP_CODES.DELETE, |
|
path : path, |
|
version : version |
|
}); |
|
|
|
return this; |
|
}; |
|
|
|
/** |
|
* Execute the transaction atomically. |
|
* |
|
* @method commit |
|
* @param callback {Function} callback function. |
|
*/ |
|
Transaction.prototype.commit = function (callback) { |
|
assert(typeof callback === 'function', 'callback must be a function'); |
|
|
|
var self = this, |
|
header = new jute.protocol.RequestHeader(), |
|
payload = new jute.TransactionRequest(this.ops), |
|
request; |
|
|
|
header.type = jute.OP_CODES.MULTI; |
|
request = new jute.Request(header, payload); |
|
|
|
this.connectionManager.queue(request, function (error, response) { |
|
if (error) { |
|
callback(error); |
|
return; |
|
} |
|
|
|
var result, |
|
i; |
|
|
|
for (i = 0; i < response.payload.results.length; i += 1) { |
|
result = response.payload.results[i]; |
|
|
|
// Find if there is an op which caused the transaction to fail. |
|
if (result.type === jute.OP_CODES.ERROR && |
|
result.err !== Exception.OK) { |
|
error = Exception.create(result.err); |
|
break; |
|
} |
|
} |
|
|
|
callback(error, response.payload.results); |
|
}); |
|
}; |
|
|
|
|
|
module.exports = Transaction;
|
|
|