diff --git a/lib/change.js b/lib/change.js index 0aeeceb..9c0f36e 100644 --- a/lib/change.js +++ b/lib/change.js @@ -6,95 +6,100 @@ var Attribute = require('./attribute'); var Protocol = require('./protocol'); - ///--- API function Change(options) { if (options) { - if (typeof (options) !== 'object') - throw new TypeError('options must be an object'); - if (options.operation && typeof (options.operation) !== 'string') - throw new TypeError('options.operation must be a string'); + assert.object(options); + assert.optionalString(options.operation); } else { options = {}; } - var self = this; this._modification = false; - - this.__defineGetter__('operation', function () { - switch (self._operation) { - case 0x00: return 'add'; - case 0x01: return 'delete'; - case 0x02: return 'replace'; - default: - throw new Error('0x' + self._operation.toString(16) + ' is invalid'); - } - }); - this.__defineSetter__('operation', function (val) { - if (typeof (val) !== 'string') - throw new TypeError('operation must be a string'); - - switch (val.toLowerCase()) { - case 'add': - self._operation = 0x00; - break; - case 'delete': - self._operation = 0x01; - break; - case 'replace': - self._operation = 0x02; - break; - default: - 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; - return; - } - // Does it have an attribute-like structure - if (Object.keys(attr).length == 2 && - typeof (attr.type) === 'string' && - Array.isArray(attr.vals)) { - self._modification = new Attribute({ - type: attr.type, - vals: attr.vals - }); - return; - } - - var keys = Object.keys(attr); - if (keys.length > 1) - throw new Error('Only one attribute per Change allowed'); - - keys.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, - modification: self._modification ? self._modification.json : {} - }; - }); - this.operation = options.operation || options.type || 'add'; this.modification = options.modification || {}; } -module.exports = Change; +Object.defineProperties(Change.prototype, { + operation: { + get: function getOperation() { + switch (this._operation) { + case 0x00: return 'add'; + case 0x01: return 'delete'; + case 0x02: return 'replace'; + default: + throw new Error('0x' + this._operation.toString(16) + ' is invalid'); + } + }, + set: function setOperation(val) { + assert.string(val); + switch (val.toLowerCase()) { + case 'add': + this._operation = 0x00; + break; + case 'delete': + this._operation = 0x01; + break; + case 'replace': + this._operation = 0x02; + break; + default: + throw new Error('Invalid operation type: 0x' + val.toString(16)); + } + }, + configurable: false + }, + modification: { + get: function getModification() { + return this._modification; + }, + set: function setModification(val) { + if (Attribute.isAttribute(val)) { + this._modification = val; + return; + } + // Does it have an attribute-like structure + if (Object.keys(val).length == 2 && + typeof (val.type) === 'string' && + Array.isArray(val.vals)) { + this._modification = new Attribute({ + type: val.type, + vals: val.vals + }); + return; + } + + var keys = Object.keys(val); + if (keys.length > 1) { + throw new Error('Only one attribute per Change allowed'); + } else if (keys.length === 0) { + return; + } + + var k = keys[0]; + console.log(keys); + var _attr = new Attribute({type: k}); + if (Array.isArray(val[k])) { + val[k].forEach(function (v) { + _attr.addValue(v.toString()); + }); + } else { + _attr.addValue(val[k].toString()); + } + this._modification = _attr; + }, + configurable: false + }, + json: { + get: function getJSON() { + return { + operation: this.operation, + modification: this._modification ? this._modification.json : {} + }; + }, + configurable: false + } +}); Change.isChange = function isChange(change) { if (!change || typeof (change) !== 'object') { @@ -206,3 +211,8 @@ Change.prototype.toBer = function (ber) { return ber; }; + + +///--- Exports + +module.exports = Change; diff --git a/test/change.test.js b/test/change.test.js index 7523b7d..a6ea7f5 100644 --- a/test/change.test.js +++ b/test/change.test.js @@ -32,7 +32,7 @@ test('new no args', function (t) { test('new with args', function (t) { var change = new Change({ - operation: 0x00, + operation: 'add', modification: new Attribute({ type: 'cn', vals: ['foo', 'bar'] @@ -50,19 +50,35 @@ test('new with args', function (t) { }); +test('validate fields', function (t) { + var c = new Change(); + t.ok(c); + t.throws(function () { + c.operation = 'bogus'; + }); + t.throws(function () { + c.modification = {too: 'many', fields: 'here'}; + }); + c.modification = { + foo: ['bar', 'baz'] + }; + t.ok(c.modification); + t.end(); +}); + + test('GH-31 (multiple attributes per Change)', function (t) { - try { - t.notOk(new Change({ + t.throws(function () { + var c = new Change({ operation: 'replace', modification: { cn: 'foo', sn: 'bar' } - }), 'should have thrown'); - } catch (e) { - t.ok(e); - t.end(); - } + }); + t.notOk(c); + }); + t.end(); });