Allow plain JS objects in client APIs where it makes sense

This commit is contained in:
Mark Cavage 2011-08-04 14:25:12 -07:00
parent 62351e06b7
commit 1042048aaf
5 changed files with 133 additions and 40 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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
});

View File

@ -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);

View File

@ -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) {