Repository: basho/riak-nodejs-client
Pull request #35 (2015-04-02 16:11:12)

CLIENTS-306: Add links to RiakObject
Added links to RiakObject for legacy support.  Tests updated. 

Also fixed some docs. 


Patch:
diff --git a/lib/client.js b/lib/client.js
index 1e9a531..7067057 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -370,11 +370,13 @@ Client.prototype.storeSchema = function(options, callback) {
 /**
  * See {{#crossLink "MapReduce"}}{{/crossLink}}
  * @method mapReduce
- * @param {Object} options The options for this operation.
+ * @param {String} query The map-reduce query. 
  * @param {Function} callback The callback to be executed when the operation completes.
+ * @param {Boolean} [stream=true] Stream the results or accumulate before calling callback.
+ * 
  */
-Client.prototype.mapReduce = function(options, callback) {
-    var cmd = new MR.MapReduce(options, callback);
+Client.prototype.mapReduce = function(query, callback, stream) {
+    var cmd = new MR.MapReduce(options, callback, stream);
     this.cluster.execute(cmd);
 };
 
diff --git a/lib/commands/kv/listbuckets.js b/lib/commands/kv/listbuckets.js
index 8d3e938..9e5760b 100644
--- a/lib/commands/kv/listbuckets.js
+++ b/lib/commands/kv/listbuckets.js
@@ -40,7 +40,7 @@ var Joi = require('joi');
  * @constructor
  * @param {Object} options The options for this command
  * @param {String} [options.bucketType=default] The bucket type in riak.
- * @param {Boolean} [stream=true] Whether to stream or accumulate the result before calling callback
+ * @param {Boolean} [options.stream=true] Whether to stream or accumulate the result before calling callback
  * @param {Number} [options.timeout] Set a timeout for this operation.
  * @param {Function} callback The callback to be executed when the operation completes.
  * @param {String} callback.err An error message. Will be null if no error.
diff --git a/lib/commands/kv/riakobject.js b/lib/commands/kv/riakobject.js
index edafc37..ef2c13f 100644
--- a/lib/commands/kv/riakobject.js
+++ b/lib/commands/kv/riakobject.js
@@ -16,6 +16,7 @@
 
 var RpbContent = require('../../protobuf/riakprotobuf').getProtoFor('RpbContent');
 var RpbPair = require('../../protobuf/riakprotobuf').getProtoFor('RpbPair');
+var RpbLink = require('../../protobuf/riakprotobuf').getProtoFor('RpbLink');
 
 /**
  * Provides the RiakObject class.
@@ -24,6 +25,11 @@ var RpbPair = require('../../protobuf/riakprotobuf').getProtoFor('RpbPair');
 
 /**
  * A class that encapsulates the metadata and value stored in Riak.
+ * 
+ * While you can fetch and store regular JS objects with {{#crossLink "FetchValue"}}{{/crossLink}}
+ * and {{#crossLink "StoreValue"}}{{/crossLink}}, if you want access to the associated 
+ * metadata stored in Riak with the value you'll want to use a RiakObject instead. 
+ *
  * @class RiakObject
  * @constructor
  */
@@ -72,6 +78,7 @@ RiakObject.prototype = {
     },
     /**
      * Set the bucket type.
+     * 
      * If this is not set 'default' is used.
      * @method setBucketType
      * @param {String} bucketType the bucket type in Riak.
@@ -101,7 +108,8 @@ RiakObject.prototype = {
     },
     /**
      * Get the value.
-     * This will either be a Buffer or a plain JS object
+     * 
+     * This will either be a Buffer or a plain JS object.
      * @method getValue
      * @return {Buffer|Object} The value returned from Riak.
      */
@@ -109,9 +117,12 @@ RiakObject.prototype = {
         return this.value;
     },
     /**
-     * Set the content type. 
+     * Set the content type.
+     *  
      * Due to Riak's HTTP API this is represented as a string suitable for
-     * a HTTP Content-Type header. If not set, the default is 'application/json'
+     * a HTTP Content-Type header. 
+     * 
+     * If not set, the default is 'application/json'
      * @method setContentType
      * @param {String} contentType the content type.
      * @chainable
@@ -122,6 +133,7 @@ RiakObject.prototype = {
     },
     /**
      * Get the content type.
+     * 
      * Due to Riak's HTTP API this is represented as a string suitable for
      * a HTTP Content-Type header. 
      * @method getContentType
@@ -132,6 +144,7 @@ RiakObject.prototype = {
     },
     /**
      * Set the user meta data.
+     * 
      * This is an array of key/value objects.
      * @method setUserMeta
      * @param {Object[]} userMeta
@@ -144,7 +157,7 @@ RiakObject.prototype = {
         return this;
     },
     /**
-     * Determine if any user meta dta is present.
+     * Determine if any user metadata is present.
      * @method hasUserMeta
      * @return {Boolean} true if user meta data is present.
      */
@@ -153,15 +166,16 @@ RiakObject.prototype = {
     },
     /**
      * Get the user meta data
+     * 
      * This is an array of key/value objects
-     * @method getUserMeta.
+     * @method getUserMeta
      * @return {Object[]} array of key/value objects
      */
     getUserMeta : function() {
         return this.userMeta;
     },
     /**
-     * Set an index, replacing the existing value is any.
+     * Set an index, replacing the existing value if any.
      * @method setIndex
      * @param {String} indexName the index name
      * @param {String[]|Number[]} arrayOfKeys - the keys 
@@ -206,6 +220,7 @@ RiakObject.prototype = {
     },
     /**
      * Add one or more keys to an index.
+     * 
      * If the index does not exist it will be created.
      * @method addToIndex
      * @param {String} indexName the index name
@@ -224,6 +239,54 @@ RiakObject.prototype = {
         }
         return this;
     },
+    /**
+     * Determine if any links are present.
+     * 
+     * Note that link walking is a deprecated feature in Riak 2.0.
+     * 
+     * See: [Link Walking](http://docs.basho.com/riak/latest/dev/using/link-walking/)
+     * @method hasLinks
+     * @return {Boolean} true if there are any links.
+     * @deprecated Link walking is a deprecated feature in Riak 2.0.
+     */
+    hasLinks : function() {
+        return this.links !== undefined;
+    },
+    /**
+     * Get the links.
+     * 
+     * This is an array of objects representing links to other objects in Riak.
+     * 
+     * Note that link walking is a deprecated feature in Riak 2.0.
+     * 
+     * See: [Link Walking](http://docs.basho.com/riak/latest/dev/using/link-walking/)
+     * @method getLinks
+     * @return {Object[]} An array containing the links, or undefined if none exist.
+     * @deprecated Link walking is a deprecated feature in Riak 2.0.
+     */
+    getLinks : function() {
+        return this.links;
+    },
+    /**
+     * Set the links.
+     * 
+     * This is an array of objects representing links to other objects in Riak.
+     * 
+     * Note that link walking is a deprecated feature in Riak 2.0.
+     * 
+     * See: [Link Walking](http://docs.basho.com/riak/latest/dev/using/link-walking/)
+     * @method setLinks
+     * @param {Object[]} links An array of objects representing the links.
+     * @param {String} links.bucket The bucket the linked object is in.
+     * @param {String} links.key The key for the linked object.
+     * @param {String} links.tag The identifier that describes the relationship you are wishing to capture with your link
+     * @chainable
+     * @deprecated Link walking is a deprecated feature in Riak 2.0.
+     */
+    setLinks : function(links) {
+        this.links = links;
+        return this;
+    },
     /**
      * Set the vector clock.
      * @method setVClock
@@ -252,6 +315,7 @@ RiakObject.prototype = {
     },
     /**
      * Returns the last modified time of this RiakObject.
+     * 
      * The timestamp is returned as a (Unix) epoch time. 
      * @method getLastModified
      * @return {Number} the last modified time. 
@@ -323,7 +387,27 @@ module.exports.createFromRpbContent = function(rpbContent, convertToJs) {
         ro.indexes = indexes;
     }
     
-    //TODO: links
+    //links
+    var pbLinks = rpbContent.getLinks();
+    if (pbLinks.length) {
+        var links = new Array(pbLinks.length);
+        var link;
+        for (i = 0; i < pbLinks.length; i++) {
+            link = {};
+            if (pbLinks[i].bucket) {
+                link.bucket = pbLinks[i].bucket.toString('utf8');
+            }
+            if (pbLinks[i].key) {
+                link.key = pbLinks[i].key.toString('utf8');
+            }
+            if (pbLinks[i].tag) {
+                link.tag = pbLinks[i].tag.toString('utf8');
+            }
+            links[i] = link;
+        }
+        ro.links = links;
+    }
+    
     return ro;
     
 };
@@ -378,7 +462,23 @@ module.exports.populateRpbContentFromRiakObject = function(ro) {
         }
     }
     
-    
+    if (ro.hasLinks()) {
+        var links = ro.getLinks();
+        var pbLink;
+        for (i = 0; i < links.length; i++) {
+            pbLink = new RpbLink();
+            if (links[i].bucket) {
+                pbLink.setBucket(new Buffer(links[i].bucket));
+            }
+            if (links[i].key) {
+                pbLink.setKey(new Buffer(links[i].key));
+            }
+            if (links[i].tag) {
+                pbLink.setTag(new Buffer(links[i].tag));
+            }
+            rpbContent.links.push(pbLink);
+        }
+    }
     return rpbContent;
         
 };
diff --git a/lib/commands/yokozuna/deleteindex.js b/lib/commands/yokozuna/deleteindex.js
index 667c8d0..c2423a3 100644
--- a/lib/commands/yokozuna/deleteindex.js
+++ b/lib/commands/yokozuna/deleteindex.js
@@ -40,7 +40,7 @@ var Joi = require('joi');
  * 
  * @class DeleteIndex
  * @constructor
- * @param {options} The options for this command
+ * @param {Object} options The options for this command
  * @param {String} [options.indexName] The name of the index to delete.
  * @param {Function} callback The callback to be executed when the operation completes.
  * @param {String} callback.err An error message. Will be null if no error.
diff --git a/lib/commands/yokozuna/fetchindex.js b/lib/commands/yokozuna/fetchindex.js
index c8c8d24..0af5ac0 100644
--- a/lib/commands/yokozuna/fetchindex.js
+++ b/lib/commands/yokozuna/fetchindex.js
@@ -40,7 +40,7 @@ var Joi = require('joi');
  *
  * @class FetchIndex
  * @constructor
- * @param {options} the options for this command
+ * @param {Object} options the options for this command
  * @param {String} [options.indexName] the name of a specific index to fetch. If not supplied, all are returned.
  * @param {Function} callback the callback to be executed when the operation completes.
  * @param {String} callback.err error message
diff --git a/yuidoc.json b/yuidoc.json
index 173b0c2..52541d9 100644
--- a/yuidoc.json
+++ b/yuidoc.json
@@ -1,7 +1,7 @@
 {
 	"name": "Basho Riak NodeJS CLient",
 	"description": "The offical NodeJS client for Riak, supported by Basho Technologies",
-	"version": "1.0.0",
+	"version": "1.0.2",
 	"url": "http://basho.github.io/riak-nodejs-client/",
 	"options" : {
 		"paths":"./lib"


Test patch:
diff --git a/test/unit/fetchvalue.js b/test/unit/fetchvalue.js
index 106c41c..84941f3 100644
--- a/test/unit/fetchvalue.js
+++ b/test/unit/fetchvalue.js
@@ -18,6 +18,7 @@ var FetchValue = require('../../lib/commands/kv/fetchvalue');
 var RpbGetResp = require('../../lib/protobuf/riakprotobuf').getProtoFor('RpbGetResp');
 var RpbContent = require('../../lib/protobuf/riakprotobuf').getProtoFor('RpbContent');
 var RpbPair = require('../../lib/protobuf/riakprotobuf').getProtoFor('RpbPair');
+var RpbLink = require('../../lib/protobuf/riakprotobuf').getProtoFor('RpbLink');
 var RpbErrorResp = require('../../lib/protobuf/riakprotobuf').getProtoFor('RpbErrorResp');
 
 var assert = require('assert');
@@ -75,6 +76,17 @@ describe('FetchValue', function() {
             pair.setValue(new Buffer('metaValue1'));
             rpbContent.usermeta.push(pair);
             
+            var link = new RpbLink();
+            link.setBucket(new Buffer('b'));
+            link.setKey(new Buffer('k'));
+            link.setTag(new Buffer('t'));
+            rpbContent.links.push(link);
+            link = new RpbLink();
+            link.setBucket(new Buffer('b'));
+            link.setKey(new Buffer('k2'));
+            link.setTag(new Buffer('t2'));
+            rpbContent.links.push(link);
+            
             var rpbGetResp = new RpbGetResp();
             rpbGetResp.setContent(rpbContent);
             rpbGetResp.setVclock(new Buffer('1234'));
@@ -92,6 +104,12 @@ describe('FetchValue', function() {
                     assert.equal(riakObject.hasUserMeta(), true);
                     assert.equal(riakObject.getUserMeta()[0].key, 'metaKey1');
                     assert.equal(riakObject.getUserMeta()[0].value, 'metaValue1');
+                    assert.equal(riakObject.getLinks()[0].bucket, 'b');
+                    assert.equal(riakObject.getLinks()[0].key, 'k');
+                    assert.equal(riakObject.getLinks()[0].tag, 't');
+                    assert.equal(riakObject.getLinks()[1].bucket, 'b');
+                    assert.equal(riakObject.getLinks()[1].key, 'k2');
+                    assert.equal(riakObject.getLinks()[1].tag, 't2');
                     assert.equal(riakObject.getVClock().toString('utf8'), '1234');
                     done();
                 }
diff --git a/test/unit/storevalue.js b/test/unit/storevalue.js
index e24d623..fe610bf 100644
--- a/test/unit/storevalue.js
+++ b/test/unit/storevalue.js
@@ -33,6 +33,8 @@ describe('StoreValue', function() {
             riakObject.addToIndex('email_bin','roach@basho.com');
             riakObject.setContentType('application/json');
             riakObject.setValue('this is a value');
+            riakObject.setLinks([{bucket: 'b', key: 'k', tag: 't'},
+                {bucket: 'b', key: 'k2', tag: 't2'}]);
             
             var vclock = new Buffer(0);
             var storeCommand = new StoreValue.Builder()
@@ -72,6 +74,12 @@ describe('StoreValue', function() {
             assert(protobuf.getContent().getUsermeta().length === 1);
             assert.equal(protobuf.getContent().getUsermeta()[0].key.toString('utf8'), 'metaKey1');
             assert.equal(protobuf.getContent().getUsermeta()[0].value.toString('utf8'), 'metaValue1');
+            assert.equal(protobuf.getContent().getLinks()[0].bucket.toString('utf8'), 'b');
+            assert.equal(protobuf.getContent().getLinks()[0].key.toString('utf8'), 'k');
+            assert.equal(protobuf.getContent().getLinks()[0].tag.toString('utf8'), 't');
+            assert.equal(protobuf.getContent().getLinks()[1].bucket.toString('utf8'), 'b');
+            assert.equal(protobuf.getContent().getLinks()[1].key.toString('utf8'), 'k2');
+            assert.equal(protobuf.getContent().getLinks()[1].tag.toString('utf8'), 't2');
             assert.equal(protobuf.getTimeout(), 20000);
             done();