Allow plain JS objects in client APIs where it makes sense
This commit is contained in:
parent
62351e06b7
commit
1042048aaf
|
@ -88,6 +88,7 @@ Attribute.toBer = function(attr, ber) {
|
|||
|
||||
|
||||
Attribute.isAttribute = function(attr) {
|
||||
if (!attr) return false;
|
||||
if (typeof(attr) !== 'object') return false;
|
||||
if (attr instanceof Attribute) return true;
|
||||
if (!attr.type || typeof(attr.type) !== 'string') return false;
|
||||
|
|
|
@ -15,8 +15,6 @@ function Change(options) {
|
|||
throw new TypeError('options must be an object');
|
||||
if (options.operation && typeof(options.operation) !== 'string')
|
||||
throw new TypeError('options.operation must be a string');
|
||||
if (options.modification && !(options.modification instanceof Attribute))
|
||||
throw new TypeErrr('options.modification must be an Attribute');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
@ -48,7 +46,24 @@ function Change(options) {
|
|||
throw new Error('Invalid operation type: 0x' + val.toString(16));
|
||||
}
|
||||
});
|
||||
|
||||
this.__defineGetter__('modification', function() {
|
||||
return self._modification;
|
||||
});
|
||||
this.__defineSetter__('modification', function(attr) {
|
||||
if (Attribute.isAttribute(attr))
|
||||
self._modification = attr;
|
||||
Object.keys(attr).forEach(function(k) {
|
||||
var _attr = new Attribute({type: k});
|
||||
if (Array.isArray(attr[k])) {
|
||||
attr[k].forEach(function(v) {
|
||||
_attr.addValue(v.toString());
|
||||
});
|
||||
} else {
|
||||
_attr.addValue(attr[k].toString());
|
||||
}
|
||||
self._modification = _attr;
|
||||
});
|
||||
});
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
operation: self.operation,
|
||||
|
@ -57,7 +72,7 @@ function Change(options) {
|
|||
});
|
||||
|
||||
this.operation = options.operation || 'add';
|
||||
this.modification = options.modification || null;
|
||||
this.modification = options.modification || {};
|
||||
}
|
||||
module.exports = Change;
|
||||
|
||||
|
@ -67,8 +82,8 @@ Change.prototype.parse = function(ber) {
|
|||
|
||||
ber.readSequence();
|
||||
this._operation = ber.readEnumeration();
|
||||
this.modification = new Attribute();
|
||||
this.modification.parse(ber);
|
||||
this._modification = new Attribute();
|
||||
this._modification.parse(ber);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
@ -79,7 +94,7 @@ Change.prototype.toBer = function(ber) {
|
|||
|
||||
ber.startSequence();
|
||||
ber.writeEnumeration(this._operation);
|
||||
ber = this.modification.toBer(ber);
|
||||
ber = this._modification.toBer(ber);
|
||||
ber.endSequence();
|
||||
|
||||
return ber;
|
||||
|
|
|
@ -282,21 +282,21 @@ Client.prototype.bind = function(name, credentials, controls, callback) {
|
|||
/**
|
||||
* Adds an entry to the LDAP server.
|
||||
*
|
||||
* Entry can be either [Attribute] or a plain JS object where the
|
||||
* values are either a plain value or an array of values. Any value (that's
|
||||
* not an array) will get converted to a string, so keep that in mind.
|
||||
*
|
||||
* @param {String} name the DN of the entry to add.
|
||||
* @param {Array} attributes an array of Attributes to be added.
|
||||
* @param {Object} entry an array of Attributes to be added or a JS object.
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.add = function(name, attributes, controls, callback) {
|
||||
Client.prototype.add = function(name, entry, controls, callback) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (!Array.isArray(attributes))
|
||||
throw new TypeError('attributes ([Attribute]) required');
|
||||
attributes.forEach(function(a) {
|
||||
if (!Attribute.isAttribute(a))
|
||||
throw new TypeError('attributes ([Attribute]) required');
|
||||
});
|
||||
if (typeof(entry) !== 'object')
|
||||
throw new TypeError('entry (object) required');
|
||||
if (typeof(controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
|
@ -306,9 +306,31 @@ Client.prototype.add = function(name, attributes, controls, callback) {
|
|||
if (typeof(callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
|
||||
if (Array.isArray(entry)) {
|
||||
entry.forEach(function(a) {
|
||||
if (!Attribute.isAttribute(a))
|
||||
throw new TypeError('entry must be an Array of Attributes');
|
||||
});
|
||||
} else {
|
||||
var save = entry;
|
||||
|
||||
entry = [];
|
||||
Object.keys(save).forEach(function(k) {
|
||||
var attr = new Attribute({type: k});
|
||||
if (Array.isArray(save[k])) {
|
||||
save[k].forEach(function(v) {
|
||||
attr.addValue(v.toString());
|
||||
});
|
||||
} else {
|
||||
attr.addValue(save[k].toString());
|
||||
}
|
||||
entry.push(attr);
|
||||
});
|
||||
}
|
||||
|
||||
var req = new AddRequest({
|
||||
entry: dn.parse(name),
|
||||
attributes: attributes,
|
||||
attributes: entry,
|
||||
controls: controls
|
||||
});
|
||||
|
||||
|
@ -320,20 +342,16 @@ Client.prototype.add = function(name, attributes, controls, callback) {
|
|||
* Compares an attribute/value pair with an entry on the LDAP server.
|
||||
*
|
||||
* @param {String} name the DN of the entry to compare attributes with.
|
||||
* @param {String} attribute name of an attribute to check.
|
||||
* @param {String} attr name of an attribute to check.
|
||||
* @param {String} value value of an attribute to check.
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, boolean, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.compare = function(name,
|
||||
attribute,
|
||||
value,
|
||||
controls,
|
||||
callback) {
|
||||
Client.prototype.compare = function(name, attr, value, controls, callback) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (typeof(attribute) !== 'string')
|
||||
if (typeof(attr) !== 'string')
|
||||
throw new TypeError('attribute (string) required');
|
||||
if (typeof(value) !== 'string')
|
||||
throw new TypeError('value (string) required');
|
||||
|
@ -348,7 +366,7 @@ Client.prototype.compare = function(name,
|
|||
|
||||
var req = new CompareRequest({
|
||||
entry: dn.parse(name),
|
||||
attribute: attribute,
|
||||
attribute: attr,
|
||||
value: value,
|
||||
controls: controls
|
||||
});
|
||||
|
|
|
@ -26,14 +26,6 @@ function SearchEntry(options) {
|
|||
throw new TypeError('options must be an object');
|
||||
if (options.objectName && !(options.objectName instanceof dn.DN))
|
||||
throw new TypeError('options.objectName must be a DN');
|
||||
if (options.attributes && !Array.isArray(options.attributes))
|
||||
throw new TypeError('options.attributes must be an array[Attribute]');
|
||||
if (options.attributes && options.attributes.length) {
|
||||
options.attributes.forEach(function(a) {
|
||||
if (!(a instanceof Attribute))
|
||||
throw new TypeError('options.attributes must be an array[Attribute]');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
@ -42,12 +34,21 @@ function SearchEntry(options) {
|
|||
LDAPMessage.call(this, options);
|
||||
|
||||
this.objectName = options.objectName || null;
|
||||
this.attributes = options.attributes ? options.attributes.slice(0) : [];
|
||||
this.setAttributes(options.attributes || []);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'SearchEntry'; });
|
||||
this.__defineGetter__('object', function() {
|
||||
var obj = {
|
||||
dn: self.dn.toString()
|
||||
};
|
||||
self.attributes.forEach(function(a) {
|
||||
obj[a.type] = a.vals.length > 1 ? a.vals.slice() : a.vals[0];
|
||||
});
|
||||
return obj;
|
||||
});
|
||||
this.__defineGetter__('_dn', function() {
|
||||
return self.objectName.toString();
|
||||
return self.objectName;
|
||||
});
|
||||
}
|
||||
util.inherits(SearchEntry, LDAPMessage);
|
||||
|
@ -62,6 +63,35 @@ SearchEntry.prototype.addAttribute = function(attr) {
|
|||
};
|
||||
|
||||
|
||||
SearchEntry.prototype.setAttributes = function(obj) {
|
||||
if (typeof(obj) !== 'object')
|
||||
throw new TypeError('object required');
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach(function(a) {
|
||||
if (!Attribute.isAttribute(a))
|
||||
throw new TypeError('entry must be an Array of Attributes');
|
||||
});
|
||||
this.attributes = obj;
|
||||
} else {
|
||||
var self = this;
|
||||
|
||||
self.attributes = [];
|
||||
Object.keys(obj).forEach(function(k) {
|
||||
var attr = new Attribute({type: k});
|
||||
if (Array.isArray(obj[k])) {
|
||||
obj[k].forEach(function(v) {
|
||||
attr.addValue(v.toString());
|
||||
});
|
||||
} else {
|
||||
attr.addValue(obj[k].toString());
|
||||
}
|
||||
self.attributes.push(attr);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
SearchEntry.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
|
|
|
@ -81,12 +81,10 @@ test('setup', function(t) {
|
|||
server.search(SUFFIX, function(req, res, next) {
|
||||
var e = res.createSearchEntry({
|
||||
objectName: req.dn,
|
||||
attributes: [
|
||||
new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['test']
|
||||
})
|
||||
]
|
||||
attributes: {
|
||||
cn: ['unit', 'test'],
|
||||
sn: 'testy'
|
||||
}
|
||||
});
|
||||
res.send(e);
|
||||
res.send(e);
|
||||
|
@ -154,6 +152,20 @@ test('add success', function(t) {
|
|||
});
|
||||
|
||||
|
||||
test('add success with object', function(t) {
|
||||
var entry = {
|
||||
cn: ['unit', 'add'],
|
||||
sn: 'test'
|
||||
};
|
||||
client.add('cn=add, ' + SUFFIX, entry, function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('compare success', function(t) {
|
||||
client.compare('cn=compare, ' + SUFFIX, 'cn', 'test', function(err,
|
||||
matched,
|
||||
|
@ -238,6 +250,22 @@ test('modify success', function(t) {
|
|||
});
|
||||
|
||||
|
||||
test('modify change plain object success', function(t) {
|
||||
var change = new Change({
|
||||
type: 'Replace',
|
||||
modification: {
|
||||
cn: 'test'
|
||||
}
|
||||
});
|
||||
client.modify('cn=modify, ' + SUFFIX, change, function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('modify array success', function(t) {
|
||||
var changes = [
|
||||
new Change({
|
||||
|
@ -294,6 +322,7 @@ test('search basic', function(t) {
|
|||
t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX);
|
||||
t.ok(entry.attributes);
|
||||
t.ok(entry.attributes.length);
|
||||
t.ok(entry.object);
|
||||
gotEntry++;
|
||||
});
|
||||
res.on('error', function(err) {
|
||||
|
|
Loading…
Reference in New Issue