Repository: basho/riak-nodejs-client
Pull request #87 (2015-07-16 18:54:23)

Allow binary riak keys
See original PR #86 


Patch:
diff --git a/README.md b/README.md
index 63ea4be..60cc129 100644
--- a/README.md
+++ b/README.md
@@ -66,3 +66,4 @@ Thank you to all of our contributors!
 * [Colin Hemmings](https://github.com/gonzohunter)
 * [Timothy Stonis](https://github.com/tstonis)
 * [Aleksandr Popov](https://github.com/mogadanez)
+* [Josh Yudaken](https://github.com/qix)
diff --git a/lib/commands/crdt/fetchcounter.js b/lib/commands/crdt/fetchcounter.js
index 2fb5112..d5ab76c 100644
--- a/lib/commands/crdt/fetchcounter.js
+++ b/lib/commands/crdt/fetchcounter.js
@@ -113,7 +113,7 @@ FetchCounter.prototype.onSuccess = function(dtFetchResp) {
 var schema = Joi.object().keys({
    bucket: Joi.string().required(),
    bucketType: Joi.string().required(),
-   key: Joi.string().required(),
+   key: Joi.binary().required(),
    r: Joi.number().default(null).optional(),
    pr: Joi.number().default(null).optional(),
    notFoundOk: Joi.boolean().default(null).optional(),
diff --git a/lib/commands/crdt/fetchmap.js b/lib/commands/crdt/fetchmap.js
index 1059b3a..0ec4ecd 100644
--- a/lib/commands/crdt/fetchmap.js
+++ b/lib/commands/crdt/fetchmap.js
@@ -119,7 +119,7 @@ FetchMap.prototype.onSuccess = function(dtFetchResp) {
 var schema = Joi.object().keys({
    bucket: Joi.string().required(),
    bucketType: Joi.string().required(),
-   key: Joi.string().required(),
+   key: Joi.binary().required(),
    r: Joi.number().default(null).optional(),
    pr: Joi.number().default(null).optional(),
    notFoundOk: Joi.boolean().default(null).optional(),
diff --git a/lib/commands/crdt/fetchset.js b/lib/commands/crdt/fetchset.js
index 01e8bae..96397d6 100644
--- a/lib/commands/crdt/fetchset.js
+++ b/lib/commands/crdt/fetchset.js
@@ -135,7 +135,7 @@ var schema = Joi.object().keys({
     // bucket type is required since default probably shouldn't have a
     // datatype associated with it
     bucketType: Joi.string().required(),
-    key: Joi.string().required(),
+    key: Joi.binary().required(),
     returnRaw: Joi.boolean().default(false).optional(),
 
     // quorum
diff --git a/lib/commands/crdt/updatecounter.js b/lib/commands/crdt/updatecounter.js
index 0c02d21..b202040 100644
--- a/lib/commands/crdt/updatecounter.js
+++ b/lib/commands/crdt/updatecounter.js
@@ -131,7 +131,7 @@ UpdateCounter.prototype.onSuccess = function(dtUpdateResp) {
 var schema = Joi.object().keys({
     bucket: Joi.string().required(),
     bucketType: Joi.string().required(),
-    key: Joi.string().default(null).optional(),
+    key: Joi.binary().default(null).optional(),
     increment: Joi.number().required(),
     w: Joi.number().default(null).optional(),
     dw: Joi.number().default(null).optional(),
diff --git a/lib/commands/crdt/updatemap.js b/lib/commands/crdt/updatemap.js
index 5f7febc..bddc4c8 100644
--- a/lib/commands/crdt/updatemap.js
+++ b/lib/commands/crdt/updatemap.js
@@ -279,7 +279,7 @@ UpdateMap.prototype._populate = function(mapOp, pbMapOp) {
 var schema = Joi.object().keys({
     bucket: Joi.string().required(),
     bucketType: Joi.string().required(),
-    key: Joi.string().optional(),
+    key: Joi.binary().default(null).optional(),
     op: Joi.object().type(MapOperation).required(),
     w: Joi.number().default(null).optional(),
     dw: Joi.number().default(null).optional(),
diff --git a/lib/commands/crdt/updateset.js b/lib/commands/crdt/updateset.js
index 63e1c70..616544b 100644
--- a/lib/commands/crdt/updateset.js
+++ b/lib/commands/crdt/updateset.js
@@ -170,7 +170,7 @@ var schema = Joi.object().keys({
     // datatype associated with it
     bucketType: Joi.string().required(),
     // key is optional because Riak can generate one
-    key: Joi.string().optional(),
+    key: Joi.binary().default(null).optional(),
 
     //operations
     additions: Joi.array().default([]).optional(),
diff --git a/lib/commands/kv/deletevalue.js b/lib/commands/kv/deletevalue.js
index c7e1b76..702a523 100644
--- a/lib/commands/kv/deletevalue.js
+++ b/lib/commands/kv/deletevalue.js
@@ -131,7 +131,7 @@ DeleteValue.prototype.onSuccess = function(rpbGetResp) {
 var schema = Joi.object().keys({
     bucket: Joi.string().required(),
     bucketType: Joi.string().default('default'),
-    key: Joi.string().required(),
+    key: Joi.binary().required(),
     r: Joi.number().optional(),
     pr: Joi.number().optional(),
     w: Joi.number().optional(),
diff --git a/lib/commands/kv/fetchpreflist.js b/lib/commands/kv/fetchpreflist.js
index de4b7ef..816e4a5 100644
--- a/lib/commands/kv/fetchpreflist.js
+++ b/lib/commands/kv/fetchpreflist.js
@@ -111,7 +111,7 @@ FetchPreflist.prototype.onSuccess = function(rpbGetResp) {
 var schema = Joi.object().keys({
    bucketType: Joi.string().default('default'),
    bucket: Joi.string().required(),
-   key: Joi.string().required()
+   key: Joi.binary().required()
 });
 
 /**
diff --git a/lib/commands/kv/fetchvalue.js b/lib/commands/kv/fetchvalue.js
index ad3b487..3b965a3 100644
--- a/lib/commands/kv/fetchvalue.js
+++ b/lib/commands/kv/fetchvalue.js
@@ -176,7 +176,7 @@ FetchValue.prototype.onSuccess = function(rpbGetResp) {
 var schema = Joi.object().keys({
    bucket: Joi.string().required(),
    bucketType: Joi.string().default('default'),
-   key: Joi.string().required(),
+   key: Joi.binary().required(),
    r: Joi.number().default(null).optional(),
    pr: Joi.number().default(null).optional(),
    notFoundOk: Joi.boolean().default(false).optional(),
diff --git a/lib/commands/kv/storevalue.js b/lib/commands/kv/storevalue.js
index 3476278..601e9dd 100644
--- a/lib/commands/kv/storevalue.js
+++ b/lib/commands/kv/storevalue.js
@@ -187,7 +187,7 @@ StoreValue.prototype.onSuccess = function(rpbPutResp) {
 var schema = Joi.object().keys({
     bucket: Joi.string().optional(),
     bucketType: Joi.string().default('default'),
-    key: Joi.string().default(null).optional(),
+    key: Joi.binary().default(null).optional(),
     value: Joi.any().required(),
     w: Joi.number().default(null).optional(),
     dw: Joi.number().default(null).optional(),


Test patch:
diff --git a/test/unit/commandbase.js b/test/unit/commandbase.js
index 9b83746..d1bab13 100644
--- a/test/unit/commandbase.js
+++ b/test/unit/commandbase.js
@@ -26,11 +26,9 @@ function TestCommand(options, callback) {
     CommandBase.call(this, 'RpbGetReq', 'RpbGetResp', callback);
     var self = this;
     Joi.validate(options, schema, function(err, options) {
-
         if (err) {
             throw err;
         }
-
         self.options = options;
     });
 }
@@ -40,7 +38,7 @@ inherits(TestCommand, CommandBase);
 var schema = Joi.object().keys({
    bucketType: Joi.string().default('default'),
    bucket: Joi.string().required(),
-   key: Joi.string().required()
+   key: Joi.binary().required()
 });
 
 function TestBuilder() {}
@@ -70,7 +68,6 @@ describe('CommandBase', function() {
     function cb(err, rslt) { }
 
     describe('TestCommand', function() {
-
         it('should use callback passed as ctor argument', function(done) {
             var options = {
                 bucketType: 'maps',
@@ -83,7 +80,6 @@ describe('CommandBase', function() {
                 done();
             });
         });
-
         it('should use callback via Builder', function(done) {
             assert.doesNotThrow(function () {
                 var cmd = new TestBuilder()
@@ -95,7 +91,6 @@ describe('CommandBase', function() {
                 done();
             });
         });
-
         it('should throw when callback not passed to ctor', function(done) {
             var options = {
                 bucketType: 'maps',
@@ -107,7 +102,6 @@ describe('CommandBase', function() {
             });
             done();
         });
-
         it('should throw when callback passed via options', function(done) {
             var options = {
                 bucketType: 'maps',
@@ -120,7 +114,6 @@ describe('CommandBase', function() {
             });
             done();
         });
-
     });
 
     describe('Commands', function() {
@@ -251,6 +244,18 @@ describe('CommandBase', function() {
                         b.withKey(default_options.key);
                     }
                 },
+            'KV.FetchPreflist' : {
+                    options : {
+                        bucketType: default_options.bucketType,
+                        bucket: default_options.bucket,
+                        key: default_options.key,
+                        callback: cb
+                    },
+                    builder_func: function (b) {
+                        b.withBucket(default_options.bucket);
+                        b.withKey(default_options.bucket);
+                    }
+                },
             'KV.FetchBucketProps' : {
                     options : {
                         bucketType: default_options.bucketType,
diff --git a/test/unit/kv/deletevalue.js b/test/unit/kv/deletevalue.js
index b84ac71..3744e1a 100644
--- a/test/unit/kv/deletevalue.js
+++ b/test/unit/kv/deletevalue.js
@@ -20,11 +20,11 @@ var RpbDelResp = rpb.getProtoFor('RpbDelResp');
 var RpbErrorResp = rpb.getProtoFor('RpbErrorResp');
 
 var assert = require('assert');
+var crypto = require('crypto');
 
 describe('DeleteValue', function() {
     describe('Build', function() {
         it('should build a RpbDelReq correctly', function(done) {
-   
             var deleteValue = new DeleteValue.Builder()
                .withBucketType('bucket_type')
                .withBucket('bucket_name')
@@ -54,16 +54,26 @@ describe('DeleteValue', function() {
             assert(protobuf.getVclock().toString('utf8'), '1234');
             assert.equal(protobuf.getTimeout(), 20000);
             done();
-                        
-            
+        });
+
+        it('should build a RpbDelReq correctly with a binary key', function(done) {
+            var binaryKey = crypto.randomBytes(128);
+            var deleteValue = new DeleteValue.Builder()
+               .withBucketType('bucket_type')
+               .withBucket('bucket_name')
+               .withKey(binaryKey)
+               .withCallback(function(){})
+               .build();
+            var protobuf = deleteValue.constructPbRequest();
+            var keyBuf = protobuf.getKey().toBuffer();
+            assert(binaryKey.equals(keyBuf));
+            done();
         });
         
         it('should take a RpbDelResp and call the users callback with the response', function(done) {
-            
             // Riak doesn't actually return a RpbDelResp message - just the code. The core
             // will send null to the command onSuccess and the response sent to the user
             // is simply a boolean true.
-
             var callback = function(err, resp) {
                 assert(resp === true);
                 done();
@@ -77,7 +87,6 @@ describe('DeleteValue', function() {
                 .build();
 
             deleteValue.onSuccess(null);
-            
         });
         
         it ('should take a RpbErrorResp and call the users callback with the error message', function(done) {
@@ -100,6 +109,5 @@ describe('DeleteValue', function() {
         
             deleteValue.onRiakError(rpbErrorResp);
         });
-        
     });
 });
diff --git a/test/unit/kv/fetchpreflist.js b/test/unit/kv/fetchpreflist.js
index 71efdee..ee74f05 100644
--- a/test/unit/kv/fetchpreflist.js
+++ b/test/unit/kv/fetchpreflist.js
@@ -22,6 +22,7 @@ var RpbBucketKeyPreflistItem = rpb.getProtoFor('RpbBucketKeyPreflistItem');
 var RpbErrorResp = rpb.getProtoFor('RpbErrorResp');
 
 var assert = require('assert');
+var crypto = require('crypto');
 
 var bucketType = 'bucket_type_name';
 var bucket = 'bucket_name';
@@ -51,6 +52,20 @@ describe('FetchPreflist', function() {
 
             done();
         });
+
+        it('should build a RpbGetBucketKeyPreflistReq correctly with a binary key', function(done) {
+            var binaryKey = crypto.randomBytes(128);
+            var cmd = new FetchPreflist.Builder()
+               .withBucketType('bucket_type')
+               .withBucket('bucket_name')
+               .withKey(binaryKey)
+               .withCallback(function(){})
+               .build();
+            var protobuf = cmd.constructPbRequest();
+            var keyBuf = protobuf.getKey().toBuffer();
+            assert(binaryKey.equals(keyBuf));
+            done();
+        });
         
         it('should take a RpbGetBucketKeyPreflistResp and call the users callback with the response', function(done) {
             var partitionId = getRandom();
diff --git a/test/unit/kv/fetchvalue.js b/test/unit/kv/fetchvalue.js
index 65a5ea0..0eedb9e 100644
--- a/test/unit/kv/fetchvalue.js
+++ b/test/unit/kv/fetchvalue.js
@@ -23,6 +23,7 @@ var RpbLink = rpb.getProtoFor('RpbLink');
 var RpbErrorResp = rpb.getProtoFor('RpbErrorResp');
 
 var assert = require('assert');
+var crypto = require('crypto');
 
 function generateTestRpbContent(value, contentType) {
     var rpbContent = new RpbContent();
@@ -56,7 +57,6 @@ describe('FetchValue', function() {
 
     describe('Build', function() {
         it('should build a RpbGetReq correctly', function(done) {
-
             var vclock = new Buffer(0);
             var fetchCommand = new FetchValue.Builder()
                .withBucketType('bucket_type')
@@ -87,7 +87,20 @@ describe('FetchValue', function() {
             assert(protobuf.getIfModified().toBuffer() !== null);
             assert.equal(protobuf.getTimeout(), 20000);
             done();
+        });
 
+        it('should build a RpbGetReq correctly with a binary key', function(done) {
+            var binaryKey = crypto.randomBytes(128);
+            var cmd = new FetchValue.Builder()
+               .withBucketType('bucket_type')
+               .withBucket('bucket_name')
+               .withKey(binaryKey)
+               .withCallback(function(){})
+               .build();
+            var protobuf = cmd.constructPbRequest();
+            var keyBuf = protobuf.getKey().toBuffer();
+            assert(binaryKey.equals(keyBuf));
+            done();
         });
 
         it('should take a RpbGetResp and call the users callback with the response', function(done) {
diff --git a/test/unit/kv/storevalue.js b/test/unit/kv/storevalue.js
index ef9e131..b1f6e6a 100644
--- a/test/unit/kv/storevalue.js
+++ b/test/unit/kv/storevalue.js
@@ -23,6 +23,7 @@ var RpbPair = rpb.getProtoFor('RpbPair');
 var RpbErrorResp = rpb.getProtoFor('RpbErrorResp');
 
 var assert = require('assert');
+var crypto = require('crypto');
 
 describe('StoreValue', function() {
     describe('Build', function() {
@@ -87,6 +88,21 @@ describe('StoreValue', function() {
             assert.equal(protobuf.getTimeout(), 20000);
             done();
         });
+
+        it('should build a RpbPutReq correctly with a binary key', function(done) {
+            var binaryKey = crypto.randomBytes(128);
+            var cmd = new StoreValue.Builder()
+               .withBucketType('bucket_type')
+               .withBucket('bucket_name')
+               .withKey(binaryKey)
+               .withContent('test content')
+               .withCallback(function(){})
+               .build();
+            var protobuf = cmd.constructPbRequest();
+            var keyBuf = protobuf.getKey().toBuffer();
+            assert(binaryKey.equals(keyBuf));
+            done();
+        });
         
         it('Should take the key, bucket, type and vclock from a RiakObject', function(done) {
             var value = 'this is a value';