zookeeper
Version:
apache zookeeper client (zookeeper async API v3.5.x - v3.8.x)
810 lines (696 loc) • 22.4 kB
JavaScript
// needed to not break the interface
/* eslint-disable camelcase */
const util = require('util');
const { EventEmitter } = require('events');
const { join, posix } = require('path');
const { apply, waterfall } = require('async');
const NativeZk = require('node-gyp-build')(join(__dirname, '..')).ZooKeeper;
const { isString, isFunction, toCompatibleAcl } = require('./helper');
const Async = {
apply,
waterfall,
};
/**
* create(zookeeperConnection, '/some-path', cb)
* if there was a problem:
* cb(error)
* if the dir was created, or already exists:
* cb()
* @param {ZooKeeper} con
* @param {string} p
* @param {function} cb
*/
const create = (con, p, cb) => {
const data = 'created by zk-mkdir-p'; // just want a dir, so store something
const flags = 0; // none
con.a_create(p, data, flags, (rc, error) => {
// already exists.
if (rc === NativeZk.ZNODEEXISTS) {
return cb();
}
if (rc !== 0) {
return cb(new Error(`Zookeeper Error: code=${rc} ${error}`));
}
// sucessfully created!
return cb();
});
};
/**
* does not support ./file or /dir/../file
* @param {ZooKeeper} con
* @param {string} p
* @param {mkdirCb} callback
*/
const mkdirp = (con, p, callback) => {
const dirs = posix.normalize(p).split(posix.sep).slice(1); // remove empty string at the start.
const tasks = [];
dirs.forEach((dir, i) => {
let subpath = `/${dirs.slice(0, i).join('/')}/${dir}`;
subpath = posix.normalize(subpath); // remove extra `/` in first iteration
tasks.push(Async.apply(create, con, subpath));
});
Async.waterfall(tasks, (err) => {
if (err) return callback(err);
// succeeded!
return callback(null, true);
});
};
/**
* @class
* @extends {EventEmitter}
*/
class ZooKeeper extends EventEmitter {
/** @param {object|string} config */
constructor(config) {
super();
if (isString(config)) {
this.config = { connect: config };
} else {
this.config = config;
}
this.native = new NativeZk();
this.native.emit = (ev, a1, a2, a3) => {
this.log(`Emitting '${ev}' with args: ${a1}, ${a2}, ${a3}`);
if (ev === 'connect' || ev === 'close') {
// the event is passing the native object. need to mangle to return the wrapper
a1 = this; // eslint-disable-line no-param-reassign
}
this.emit(ev, a1, a2, a3);
};
this.encoding = null;
}
/** @param {object|boolean} logger */
setLogger(logger) {
if (logger === true) {
this.logger = (str) => {
console.info(`ZOOKEEPER_LOG: ${str}`);
};
} else if (logger === false) {
this.logger = undefined;
} else if (isFunction(logger)) {
this.logger = logger;
} else {
throw new Error('InvalidArgument: logger must be a function or true/false to utilize default logger');
}
}
/** @param {...*} args */
log(...args) {
if (this.logger) this.logger(...args);
}
/**
* @param {object|string} config
* @returns {ZooKeeper}
*/
init(config) {
if (isString(config)) {
// eslint-disable-next-line no-param-reassign
config = { connect: config };
}
if (this.config) {
// eslint-disable-next-line no-param-reassign
config = config ? Object.assign(config, this.config) : this.config;
}
this.log(`Calling init with ${util.inspect(config)}`);
if (config.data_as_buffer !== undefined) {
this.data_as_buffer = config.data_as_buffer;
this.log('Encoding for data output: %s', this.encoding);
}
this.native.init(config);
return this;
}
/**
* @param {object|function} options
* @param {connectCb} cb
*/
connect(options, cb) {
let callback;
if (isFunction(options)) {
callback = options;
this.init({});
} else {
callback = cb;
this.init(options);
}
this.errorHandler = (err) => {
this.removeListener('error', this.errorHandler);
this.removeListener('connect', this.connectHandler);
callback(err);
};
this.connectHandler = () => {
this.removeListener('error', this.errorHandler);
this.removeListener('connect', this.connectHandler);
callback(null, this);
};
this.on('error', this.errorHandler);
this.on('connect', this.connectHandler);
}
/** @returns {*} */
close() {
this.log(`Calling close with ${util.inspect([])}`);
return this.native.close();
}
/**
* @param {string} path
* @param {string|Buffer} data
* @param {number} flags - an int32 value
* @param {pathCb} pathCb
* @returns {*}
*/
a_create(path, data, flags, pathCb) {
this.log(`Calling a_create with ${util.inspect([path, data, flags, pathCb])}`);
return this.native.a_create(path, data, flags, pathCb);
}
/**
* @param {string} path
* @param {string|Buffer} data
* @param {number} flags - an int32 value
* @param {number} ttl - a positive int32 value
* @param {pathCb} pathCb
* @returns {*}
*/
a_createTtl(path, data, flags, ttl, pathCb) {
this.log(`Calling a_create_ttl with ${util.inspect([path, data, flags, ttl, pathCb])}`);
return this.native.a_create_ttl(path, data, flags, ttl, pathCb);
}
/**
* @param {string} path
* @param {boolean} watch
* @param {statCb} statCb
* @returns {*}
*/
a_exists(path, watch, statCb) {
this.log(`Calling a_exists with ${util.inspect([path, watch, statCb])}`);
return this.native.a_exists(path, watch, statCb);
}
/**
* @param {string} path
* @param {watchCb} watchCb
* @param {statCb} statCb
* @returns {*}
*/
aw_exists(path, watchCb, statCb) {
this.log(`Calling aw_exists with ${util.inspect([path, watchCb, statCb])}`);
return this.native.aw_exists(path, watchCb, statCb);
}
/**
* @param {string} path
* @param {boolean} watch
* @param {dataCb} dataCb
* @returns {*}
*/
a_get(path, watch, dataCb) {
this.log(`Calling a_get with ${util.inspect([path, watch, dataCb])}`);
return this.native.a_get(path, watch, (rc, error, stat, data) => {
if (data && this.encoding) {
return dataCb(rc, error, stat, data.toString(this.encoding));
}
return dataCb(rc, error, stat, data);
});
}
/**
* @param {string} path
* @param {watchCb} watchCb
* @param {dataCb} dataCb
* @returns {*}
*/
aw_get(path, watchCb, dataCb) {
this.log(`Calling aw_get with ${util.inspect([path, watchCb, dataCb])}`);
return this.native.aw_get(path, watchCb, (rc, error, stat, data) => {
if (data && this.encoding) {
return dataCb(rc, error, stat, data.toString(this.encoding));
}
return dataCb(rc, error, stat, data);
});
}
/**
* @param {string} path
* @param {boolean} watch
* @param {childCb} childCb
* @returns {*}
*/
a_get_children(path, watch, childCb) {
this.log(`Calling a_get_children with ${util.inspect([path, watch, childCb])}`);
return this.native.a_get_children(path, watch, childCb);
}
/**
* @param {string} path
* @param {watchCb} watchCb
* @param {childCb} childCb
* @returns {*}
*/
aw_get_children(path, watchCb, childCb) {
this.log(`Calling aw_get_children with ${util.inspect([path, watchCb, childCb])}`);
return this.native.aw_get_children(path, watchCb, childCb);
}
/**
* @param {string} path
* @param {boolean} watch
* @param {child2Cb} childCb
* @returns {*}
*/
a_get_children2(path, watch, childCb) {
this.log(`Calling a_get_children2 with ${util.inspect([path, watch, childCb])}`);
return this.native.a_get_children2(path, watch, childCb);
}
/**
* @param {string} path
* @param {watchCb} watchCb
* @param {child2Cb} childCb
* @returns {*}
*/
aw_get_children2(path, watchCb, childCb) {
this.log(`Calling aw_get_children2 with ${util.inspect([path, watchCb, childCb])}`);
return this.native.aw_get_children2(path, watchCb, childCb);
}
/**
* @param {string} path
* @param {string|Buffer} data
* @param {number} version - an int32 value
* @param {statCb} statCb
* @returns {*}
*/
a_set(path, data, version, statCb) {
this.log(`Calling a_set with ${util.inspect([path, data, version, statCb])}`);
return this.native.a_set(path, data, version, statCb);
}
/**
* @param {string} path
* @param {number} version - an int32 value
* @param {voidCb} voidCb
* @returns {*}
*/
// eslint-disable-next-line no-underscore-dangle
a_delete_(path, version, voidCb) {
this.log(`Calling a_delete_ with ${util.inspect([path, version, voidCb])}`);
// eslint-disable-next-line no-underscore-dangle
return this.native.a_delete_(path, version, voidCb);
}
/**
* @param {string} path
* @param {aclCb} aclCb
* @returns {*}
*/
a_get_acl(path, aclCb) {
this.log(`Calling a_get_acl with ${util.inspect([path, aclCb])}`);
return this.native.a_get_acl(path, aclCb);
}
/**
* @param {string} path
* @param {number} version - an int32 value
* @param {acl} acl
* @param {voidCb} voidCb
* @returns {*}
*/
a_set_acl(path, version, acl, voidCb) {
const compatibleAcl = toCompatibleAcl(acl);
this.log(`Calling a_set_acl with ${util.inspect([path, version, compatibleAcl, voidCb])}`);
return this.native.a_set_acl(path, version, compatibleAcl, voidCb);
}
/**
* @param {string} scheme
* @param {string} auth
* @param {voidCb} voidCb
* @returns {*}
*/
add_auth(scheme, auth, voidCb) {
this.log(`Calling add_auth with ${util.inspect([scheme, auth, voidCb])}`);
return this.native.add_auth(scheme, auth, voidCb);
}
/**
* @param {string} path
* @param {mkdirCb} cb
*/
mkdirp(path, cb) {
this.log(`Calling mkdirp with ${util.inspect([path, cb])}`);
return mkdirp(this, path, cb);
}
/**
* @param {string} path
* @param {function} cb
* @returns {*}
*/
a_sync(path, cb) {
this.log(`Calling a_sync ${util.inspect([path, cb])}`);
return this.native.a_sync(path, cb);
}
/**
* @param {boolean} watch
* @param {dataCb} dataCb
* @returns {*}
*/
a_getconfig(watch, dataCb) {
this.log(`Calling a_getconfig with ${util.inspect([watch, dataCb])}`);
return this.native.a_getconfig(watch, (rc, error, stat, data) => {
if (data && this.encoding) {
return dataCb(rc, error, stat, data.toString(this.encoding));
}
return dataCb(rc, error, stat, data);
});
}
/**
* @param {watchCb} watchCb
* @param {dataCb} dataCb
* @returns {*}
*/
aw_getconfig(watchCb, dataCb) {
this.log(`Calling aw_getconfig with ${util.inspect([watchCb, dataCb])}`);
return this.native.aw_getconfig(watchCb, (rc, error, stat, data) => {
if (data && this.encoding) {
return dataCb(rc, error, stat, data.toString(this.encoding));
}
return dataCb(rc, error, stat, data);
});
}
/**
* @param {string|null} joining
* @param {string|null} leaving
* @param {string|null} members
* @param {number} config_version
* @param {dataCb} dataCb
* @returns {*}
*/
a_reconfig(joining, leaving, members, config_version, dataCb) {
this.log('Calling a_reconfig with '
+ `${util.inspect([joining, leaving, members, config_version, dataCb])}`);
return this.native.a_reconfig(
joining,
leaving,
members,
config_version,
(rc, error, stat, data) => {
if (data && this.encoding) {
return dataCb(rc, error, stat, data.toString(this.encoding));
}
return dataCb(rc, error, stat, data);
},
);
}
/**
* @param {string} servers
* @returns {*}
*/
set_servers(servers) {
this.log(`Calling set_servers with ${util.inspect([servers])}`);
return this.native.set_servers(servers);
}
get state() {
return this.native.state;
}
/** @param {number} value */
set state(value) {
this.native.state = value;
}
get timeout() {
return this.native.timeout;
}
/** @param {number} value */
set timeout(value) {
this.native.timeout = value;
}
get client_id() {
return this.native.client_id;
}
/** @param {number} value */
set client_id(value) {
this.native.client_id = value;
}
get client_password() {
return this.native.client_password;
}
/** @param {string} value */
set client_password(value) {
this.native.client_password = value;
}
get is_unrecoverable() {
return this.native.is_unrecoverable;
}
/** @param {number} value */
set is_unrecoverable(value) {
this.native.is_unrecoverable = value;
}
/** @param {string} value */
setEncoding(value) {
this.encoding = value;
}
/**
* @deprecated Use setEncoding()
* @returns {boolean}
*/
get data_as_buffer() {
// If there's an encoding, then data isn't a buffer.
// If there's no encoding, then data will be a buffer.
return !this.encoding;
}
/** @param {boolean} data_as_buffer */
set data_as_buffer(data_as_buffer) {
// If the data is a buffer, then there's no encoding.
// If the data is NOT a buffer, then the default encoding is 'utf8'.
this.encoding = ((data_as_buffer === true) ? null : 'utf8');
}
/** @deprecated @returns {number} 1 */
static get ZOO_PERM_READ() {
return NativeZk.ZOO_PERM_READ;
}
/** @deprecated @returns {number} 2 */
static get ZOO_PERM_WRITE() {
return NativeZk.ZOO_PERM_WRITE;
}
/** @deprecated @returns {number} 4 */
static get ZOO_PERM_CREATE() {
return NativeZk.ZOO_PERM_CREATE;
}
/** @deprecated @returns {number} 8 */
static get ZOO_PERM_DELETE() {
return NativeZk.ZOO_PERM_DELETE;
}
/** @deprecated @returns {number} 16 */
static get ZOO_PERM_ADMIN() {
return NativeZk.ZOO_PERM_ADMIN;
}
/** @deprecated @returns {number} 31 */
static get ZOO_PERM_ALL() {
return NativeZk.ZOO_PERM_ALL;
}
/** @deprecated @returns {number} -112 */
static get ZOO_EXPIRED_SESSION_STATE() {
return NativeZk.ZOO_EXPIRED_SESSION_STATE;
}
/** @deprecated @returns {number} -113 */
static get ZOO_AUTH_FAILED_STATE() {
return NativeZk.ZOO_AUTH_FAILED_STATE;
}
/** @deprecated @returns {number} 1 */
static get ZOO_CONNECTING_STATE() {
return NativeZk.ZOO_CONNECTING_STATE;
}
/** @deprecated @returns {number} 2 */
static get ZOO_ASSOCIATING_STATE() {
return NativeZk.ZOO_ASSOCIATING_STATE;
}
/** @deprecated @returns {number} 3 */
static get ZOO_CONNECTED_STATE() {
return NativeZk.ZOO_CONNECTED_STATE;
}
/** @deprecated @returns {number} 1 */
static get ZOO_LOG_LEVEL_ERROR() {
return NativeZk.ZOO_LOG_LEVEL_ERROR;
}
/** @deprecated @returns {number} 2 */
static get ZOO_LOG_LEVEL_WARN() {
return NativeZk.ZOO_LOG_LEVEL_WARN;
}
/** @deprecated @returns {number} 3 */
static get ZOO_LOG_LEVEL_INFO() {
return NativeZk.ZOO_LOG_LEVEL_INFO;
}
/** @deprecated @returns {number} 4 */
static get ZOO_LOG_LEVEL_DEBUG() {
return NativeZk.ZOO_LOG_LEVEL_DEBUG;
}
/** @deprecated @returns {number} 1 */
static get ZOO_EPHEMERAL() {
return NativeZk.ZOO_EPHEMERAL;
}
/** @deprecated @returns {number} 2 */
static get ZOO_SEQUENCE() {
return NativeZk.ZOO_SEQUENCE;
}
/** @deprecated @returns {number} 0 */
static get ZOK() {
return NativeZk.ZOK;
}
/** @deprecated @returns {number} -1 */
static get ZSYSTEMERROR() {
return NativeZk.ZSYSTEMERROR;
}
/** @deprecated @returns {number} -2 */
static get ZRUNTIMEINCONSISTENCY() {
return NativeZk.ZRUNTIMEINCONSISTENCY;
}
/** @deprecated @returns {number} -3 */
static get ZDATAINCONSISTENCY() {
return NativeZk.ZDATAINCONSISTENCY;
}
/** @deprecated @returns {number} -4 */
static get ZCONNECTIONLOSS() {
return NativeZk.ZCONNECTIONLOSS;
}
/** @deprecated @returns {number} -5 */
static get ZMARSHALLINGERROR() {
return NativeZk.ZMARSHALLINGERROR;
}
/** @deprecated @returns {number} -6 */
static get ZUNIMPLEMENTED() {
return NativeZk.ZUNIMPLEMENTED;
}
/** @deprecated @returns {number} -7 */
static get ZOPERATIONTIMEOUT() {
return NativeZk.ZOPERATIONTIMEOUT;
}
/** @deprecated @returns {number} -8 */
static get ZBADARGUMENTS() {
return NativeZk.ZBADARGUMENTS;
}
/** @deprecated @returns {number} -9 */
static get ZINVALIDSTATE() {
return NativeZk.ZINVALIDSTATE;
}
/** @deprecated @returns {number} -100 */
static get ZAPIERROR() {
return NativeZk.ZAPIERROR;
}
/** @deprecated @returns {number} -101 */
static get ZNONODE() {
return NativeZk.ZNONODE;
}
/** @deprecated @returns {number} -102 */
static get ZNOAUTH() {
return NativeZk.ZNOAUTH;
}
/** @deprecated @returns {number} -103 */
static get ZBADVERSION() {
return NativeZk.ZBADVERSION;
}
/** @deprecated @returns {number} -108 */
static get ZNOCHILDRENFOREPHEMERALS() {
return NativeZk.ZNOCHILDRENFOREPHEMERALS;
}
/** @deprecated @returns {number} -110 */
static get ZNODEEXISTS() {
return NativeZk.ZNODEEXISTS;
}
/** @deprecated @returns {number} -111 */
static get ZNOTEMPTY() {
return NativeZk.ZNOTEMPTY;
}
/** @deprecated @returns {number} -112 */
static get ZSESSIONEXPIRED() {
return NativeZk.ZSESSIONEXPIRED;
}
/** @deprecated @returns {number} -113 */
static get ZINVALIDCALLBACK() {
return NativeZk.ZINVALIDCALLBACK;
}
/** @deprecated @returns {number} -114 */
static get ZINVALIDACL() {
return NativeZk.ZINVALIDACL;
}
/** @deprecated @returns {number} -115 */
static get ZAUTHFAILED() {
return NativeZk.ZAUTHFAILED;
}
/** @deprecated @returns {number} -116 */
static get ZCLOSING() {
return NativeZk.ZCLOSING;
}
/** @deprecated @returns {number} -117 */
static get ZNOTHING() {
return NativeZk.ZNOTHING;
}
/** @deprecated @returns {number} -118 */
static get ZSESSIONMOVED() {
return NativeZk.ZSESSIONMOVED;
}
/** @deprecated @returns {{perms:number, scheme:string, auth:string}} */
static get ZOO_OPEN_ACL_UNSAFE() {
return NativeZk.ZOO_OPEN_ACL_UNSAFE;
}
/** @deprecated @returns {{perms:number, scheme:string, auth:string}} */
static get ZOO_READ_ACL_UNSAFE() {
return NativeZk.ZOO_READ_ACL_UNSAFE;
}
/** @deprecated @returns {{perms:number, scheme:string, auth:string}} */
static get ZOO_CREATOR_ALL_ACL() {
return NativeZk.ZOO_CREATOR_ALL_ACL;
}
/** @deprecated @returns {number} 1 */
static get ZOO_CREATED_EVENT() {
return NativeZk.ZOO_CREATED_EVENT;
}
/** @deprecated @returns {number} 2 */
static get ZOO_DELETED_EVENT() {
return NativeZk.ZOO_DELETED_EVENT;
}
/** @deprecated @returns {number} 3 */
static get ZOO_CHANGED_EVENT() {
return NativeZk.ZOO_CHANGED_EVENT;
}
/** @deprecated @returns {number} 4 */
static get ZOO_CHILD_EVENT() {
return NativeZk.ZOO_CHILD_EVENT;
}
/** @deprecated @returns {number} -1 */
static get ZOO_SESSION_EVENT() {
return NativeZk.ZOO_SESSION_EVENT;
}
/** @deprecated @returns {number} -2 */
static get ZOO_NOTWATCHING_EVENT() {
return NativeZk.ZOO_NOTWATCHING_EVENT;
}
/** @deprecated @returns {number} 1 */
static get ZOOKEEPER_WRITE() {
return NativeZk.ZOOKEEPER_WRITE;
}
/** @deprecated @returns {number} 2 */
static get ZOOKEEPER_READ() {
return NativeZk.ZOOKEEPER_READ;
}
/** @deprecated @returns {string} "/zookeeper/config" */
static get ZOO_CONFIG_NODE() {
return NativeZk.ZOO_CONFIG_NODE;
}
/** @deprecated use strings directly */
static get on_closed() {
return 'close';
}
/** @deprecated use strings directly */
static get on_connected() {
return 'connect';
}
/** @deprecated use strings directly */
static get on_connecting() {
return 'connecting';
}
/** @deprecated use strings directly */
static get on_event_created() {
return 'created';
}
/** @deprecated use strings directly */
static get on_event_deleted() {
return 'deleted';
}
/** @deprecated use strings directly */
static get on_event_changed() {
return 'changed';
}
/** @deprecated use strings directly */
static get on_event_child() {
return 'child';
}
/** @deprecated use strings directly */
static get on_event_notwatching() {
return 'notwatching';
}
}
/** @type {ZooKeeper} */
module.exports = ZooKeeper;