javascript/syncbase: Allowing methods that take
RowRange/PrefixRange to also accept strings.
When a string is used, it is interpreted
as 'prefix'.

scan and deleteRange accept string in addition
to RowRange. When a string is used, it is assumed
to be a prefix rather than a single row key.
This is a reasonable assumption since deleteRange
has the sister method delete(string key) that can
be used for deleting single rows and scanning for
a single row is equivalent to doing a get.

Now we are also removing PrefixRange completely
since prefix is now just a string for scan,
deleteRange and setPermission.

PrefixPermission also now has the prefix property
as string instead of PrefixRange.

Closes https://github.com/vanadium/issues/issues/677

Change-Id: If1c611ef6f5d0bc88ad8c2b45df2784d32ecf276
diff --git a/src/nosql/rowrange.js b/src/nosql/rowrange.js
index 98ed55a..c85c9ad 100644
--- a/src/nosql/rowrange.js
+++ b/src/nosql/rowrange.js
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-var inherits = require('inherits');
-
 var util = require('../util');
 
 /**
@@ -16,7 +14,8 @@
 module.exports = {
   range: range,
   singleRow: singleRow,
-  prefix: prefix
+  prefix: prefix,
+  RowRange: RowRange
 };
 
 /**
@@ -37,11 +36,14 @@
 /**
  * Creates a range that only matches items of the given prefix.
  * @param {string} prefix Prefix.
- * @return {module:syncbase.nosql~PrefixRange} A PrefixRange object. PrefixRange
+ * @return {module:syncbase.nosql~Range} A Range object covering the prefix.
  * inherits from {@link module:syncbase.nosql~RowRange}
  */
 function prefix(p) {
-  return new PrefixRange(p);
+  var startBytes = util.stringToUTF8Bytes(p);
+  var limitBytes = util.stringToUTF8Bytes(p);
+  util.prefixRangeLimit(limitBytes);
+  return new RowRange(startBytes, limitBytes);
 }
 
 var ASCII_NULL = '\x00';
@@ -93,37 +95,3 @@
     enumerable: true
   });
 }
-
-/*
- * @summary
- * PrefixRange is a sub type of {@link module:syncbase.nosql.rowrange~RowRange}
- * that indicates all ranges matching a prefix.
- * Private constructor, use {@link module:syncbase.nosql.rowrange#prefix} to
- * create an instance.
- * @inherits module:syncbase.nosql.rowrange~RowRange
- * @inner
- * @constructor
- * @memberof {module:syncbase.nosql.rowrange}
- */
-function PrefixRange(prefix) {
-  if (!(this instanceof PrefixRange)) {
-    return new PrefixRange(prefix);
-  }
-
-  var startBytes = util.stringToUTF8Bytes(prefix);
-  var limitBytes = util.stringToUTF8Bytes(prefix);
-  util.prefixRangeLimit(limitBytes);
-
-  /**
-   * Prefix
-   * @type {string}
-   */
-  Object.defineProperty(this, 'prefix', {
-    value: prefix,
-    writable: false,
-    enumerable: true
-  });
-
-  RowRange.call(this, startBytes, limitBytes);
-}
-inherits(PrefixRange, RowRange);
diff --git a/src/nosql/table.js b/src/nosql/table.js
index 81037b1..9f83221 100644
--- a/src/nosql/table.js
+++ b/src/nosql/table.js
@@ -6,8 +6,10 @@
 var vanadium = require('vanadium');
 
 var nosqlVdl = require('../gen-vdl/v.io/v23/services/syncbase/nosql');
-var prefix = require('./rowrange').prefix;
 var Row = require('./row');
+var RowRange = require('./rowrange');
+
+var prefix = RowRange.prefix;
 
 module.exports = Table;
 
@@ -147,10 +149,13 @@
  * deleteRange deletes all rows in the given half-open range [start, limit). If
  * limit is "", all rows with keys >= start are included.
  * @param {module:vanadium.context.Context} ctx Vanadium context.
- * @param {module:syncbase.nosql.rowrange.RowRange} range Row ranges to delete.
+ * @param {module:syncbase.nosql.rowrange.RowRange|string} range Row range
+ * to delete. If a string value is provided for the range, it is assumed to be
+ * a prefix.
  * @param {function} cb Callback.
  */
 Table.prototype.deleteRange = function(ctx, range, cb) {
+  range = normalizeRangeParam(range);
   this._wire(ctx).deleteRange(
         ctx, this.schemaVersion, range.start, range.limit, cb);
 };
@@ -162,11 +167,14 @@
  * time of the RPC, and will not reflect subsequent writes to keys not yet
  * reached by the stream.
  * @param {module:vanadium.context.Context} ctx Vanadium context.
- * @param {module:syncbase.nosql.rowrange.RowRange} range Row ranges to scan.
+ * @param {module:syncbase.nosql.rowrange.RowRange|string} range Row range to
+ * scan. If a string value is provided for the range, it is assumed to be
+ * a prefix.
  * @param {function} cb Callback.
  * @returns {stream} Stream of row objects.
  */
 Table.prototype.scan = function(ctx, range, cb) {
+  range = normalizeRangeParam(range);
   var vomStreamDecoder = through2({
     objectMode: true
   }, function(row, enc, cb) {
@@ -201,19 +209,16 @@
  * SetPermissions will fail if called with a prefix that does not match any
  * rows.
  * @param {module:vanadium.context.Context} ctx Vanadium context.
- * @param {module:syncbase.nosql.rowrange.PrefixRange|string} prefix Prefix or
- * PrefixRange.
+ * @param {string} prefix Prefix.
  * @param @param {module:vanadium.security.access.Permissions} perms Permissions
  * for the rows matching the prefix.
  * @param {function} cb Callback.
  */
 Table.prototype.setPermissions = function(ctx, prefix, perms, cb) {
   this._wire(ctx).setPermissions(
-        ctx, this.schemaVersion, stringifyPrefix(prefix), perms, cb);
+        ctx, this.schemaVersion, prefix, perms, cb);
 };
 
-
-
 /**
  * GetPermissions returns an array of (prefix, perms) pairs. The array is
  * sorted from longest prefix to shortest, so element zero is the one that
@@ -225,80 +230,35 @@
  * @param {function} cb Callback.
  */
 Table.prototype.getPermissions = function(ctx, key, cb) {
-  // There are two PrefixPermission types, one is the wire type which has
-  // Prefix as a string and then there is the client type where prefix is a
-  // PrefixRange, therefore we convert between the wire and client types.
-  this._wire(ctx).getPermissions(ctx, this.schemaVersion, key,
-      function(err, wirePerms) {
-        if (err) {
-          return cb(err);
-        }
-
-        var perms = wirePerms.map(function(v) {
-          return new PrefixPermissions(
-            prefix(v.prefix),
-            v.perms
-          );
-        });
-
-        cb(null, perms);
-      }
-  );
+  this._wire(ctx).getPermissions(ctx, this.schemaVersion, key, cb);
 };
 
 /**
  * DeletePermissions deletes the permissions for the specified prefix. Any
  * rows covered by this prefix will use the next longest prefix's permissions.
  * @param {module:vanadium.context.Context} ctx Vanadium context.
- * @param {module:syncbase.nosql.rowrane.PrefixRange|string} prefix Prefix or
- * PrefixRange.
+ * @param {string} prefix Prefix.
  * @param {function} cb Callback.
  */
 Table.prototype.deletePermissions = function(ctx, prefix, cb) {
-  //TODO(aghassemi): Why is prefix a PrefixRange in Go?
   this._wire(ctx).deletePermissions(
-        ctx, this.schemaVersion, stringifyPrefix(prefix), cb);
+        ctx, this.schemaVersion, prefix, cb);
 };
 
-function stringifyPrefix(prefix) {
-  var prefixStr = prefix;
-  if (typeof prefix === 'object') {
-    // assume it is a PrefixRange
-    prefixStr = prefix.prefix;
-  }
-  return prefixStr;
-}
-
 /**
- * @summary
- * Represents a pair of {@link module:syncbase.nosql~PrefixRange} and
- * {@link module:vanadium.security.access.Permissions}.
- * @constructor
- * @inner
- * @memberof {module:syncbase.nosql}
+ * Ensures range is either a string or a RowRange object.
+ * If a string, it returns a prefix RowRange object.
+ * If not a string or RowRange object, it throws a type error.
+ * @private
  */
-function PrefixPermissions(prefixRange, perms) {
-  if (!(this instanceof PrefixPermissions)) {
-    return new PrefixPermissions(prefixRange, perms);
+function normalizeRangeParam(range) {
+  if (typeof range === 'string') {
+    range = prefix(range);
+  } else if(!(range instanceof RowRange.RowRange)) {
+    var rangeType = (range.constructor ? range.constructor.name : typeof range);
+    throw new TypeError('range must be of type string or RowRange. got ' +
+      range + ' with type ' + rangeType);
   }
 
-  /**
-   * Prefix
-   * @type {module:syncbase.nosql~PrefixRange}
-   */
-  Object.defineProperty(this, 'prefix', {
-    value: prefixRange,
-    writable: false,
-    enumerable: true
-  });
-
-  /**
-   * Permissions
-   * @type {module:vanadium.security.access.Permissions}
-   */
-  Object.defineProperty(this, 'perms', {
-    value: perms,
-    writable: false,
-    enumerable: true
-  });
+  return range;
 }
diff --git a/test/integration/test-table.js b/test/integration/test-table.js
index 3e24c89..86b2fdd 100644
--- a/test/integration/test-table.js
+++ b/test/integration/test-table.js
@@ -241,7 +241,16 @@
   });
 });
 
-test('Scanning table by a prefix range', function(t) {
+test('Scanning table by a prefix range passed as string', function(t) {
+  testScanningTableByPrefix(t, ROW_KEY);
+});
+
+test('Scanning table by a prefix range passed as RowRange', function(t) {
+  var range = syncbase.nosql.rowrange.prefix(ROW_KEY);
+  testScanningTableByPrefix(t, range);
+});
+
+function testScanningTableByPrefix(t, range) {
   setupTable(t, function(err, o) {
     if (err) {
       return t.end(err);
@@ -275,23 +284,30 @@
         return o.teardown(t.end);
       }
 
-      var range = syncbase.nosql.rowrange.prefix(ROW_KEY);
       assertScanRows(o.ctx, table, range, prefixedRows, function(err) {
         t.error(err);
         o.teardown(t.end);
       });
     }
   });
+}
+
+test('Deleting rows by a prefix range passed as string', function(t) {
+  testDeletingRowsByPrefix(t, ROW_KEY);
 });
 
-test('Deleting rows by a prefix range', function(t) {
+test('Deleting rows by a prefix range passed as RowRange', function(t) {
+  var range = syncbase.nosql.rowrange.prefix(ROW_KEY);
+  testDeletingRowsByPrefix(t, range);
+});
+
+function testDeletingRowsByPrefix(t, range) {
   setupTable(t, function(err, o) {
     if (err) {
       return t.end(err);
     }
 
     var table = o.table;
-    var range = syncbase.nosql.rowrange.prefix(ROW_KEY);
 
     // create multiple rows all with ROW_KEY as prefix
     var rows = [{
@@ -328,7 +344,7 @@
       });
     }
   });
-});
+}
 
 //TODO(aghassemi) Skipped test.
 //Set permission for prefix != "" is not implemented.