syncbase: naming change follow-ups

MultiPart: 4/7
Change-Id: I9d0f9e317aa7df18907a22befad8b924d5dc8751
diff --git a/src/gen-vdl/v.io/v23/services/syncbase/index.js b/src/gen-vdl/v.io/v23/services/syncbase/index.js
index 9102a01..580d040 100644
--- a/src/gen-vdl/v.io/v23/services/syncbase/index.js
+++ b/src/gen-vdl/v.io/v23/services/syncbase/index.js
@@ -20,11 +20,6 @@
 
 
 // Types:
-var _type1 = new vdl.Type();
-_type1.kind = vdl.kind.LIST;
-_type1.name = "";
-_type1.elem = vdl.types.STRING;
-_type1.freeze();
 
 
 
@@ -53,11 +48,6 @@
 
     
       
-Service.prototype.listApps = function(ctx, serverCall) {
-  throw new Error('Method ListApps not implemented');
-};
-    
-      
 Service.prototype.setPermissions = function(ctx, serverCall, perms, version) {
   throw new Error('Method SetPermissions not implemented');
 };
@@ -83,22 +73,6 @@
     
       
     {
-    name: 'ListApps',
-    doc: "// ListApps returns a list of all App names.\n// TODO(sadovsky): Maybe switch to streaming RPC.",
-    inArgs: [],
-    outArgs: [{
-      name: '',
-      doc: "",
-      type: _type1
-    },
-    ],
-    inStream: null,
-    outStream: null,
-    tags: [canonicalize.reduce(new access.Tag("Read", true), new access.Tag()._type), ]
-  },
-    
-      
-    {
     name: 'SetPermissions',
     doc: "// SetPermissions replaces the current Permissions for an object.  version\n// allows for optional, optimistic concurrency control.  If non-empty,\n// version's value must come from GetPermissions.  If any client has\n// successfully called SetPermissions in the meantime, the version will be\n// stale and SetPermissions will fail.  If empty, SetPermissions performs an\n// unconditional update.\n//\n// Permissions objects are expected to be small.  It is up to the\n// implementation to define the exact limit, though it should probably be\n// around 100KB.  Large lists of principals can be represented concisely using\n// blessings.\n//\n// There is some ambiguity when calling SetPermissions on a mount point.\n// Does it affect the mount itself or does it affect the service endpoint\n// that the mount points to?  The chosen behavior is that it affects the\n// service endpoint.  To modify the mount point's Permissions, use\n// ResolveToMountTable to get an endpoint and call SetPermissions on that.\n// This means that clients must know when a name refers to a mount point to\n// change its Permissions.",
     inArgs: [{
@@ -164,11 +138,6 @@
 };
     
       
-App.prototype.listDatabases = function(ctx, serverCall) {
-  throw new Error('Method ListDatabases not implemented');
-};
-    
-      
 App.prototype.setPermissions = function(ctx, serverCall, perms, version) {
   throw new Error('Method SetPermissions not implemented');
 };
@@ -232,23 +201,7 @@
     ],
     inStream: null,
     outStream: null,
-    tags: [canonicalize.reduce(new access.Tag("Read", true), new access.Tag()._type), ]
-  },
-    
-      
-    {
-    name: 'ListDatabases',
-    doc: "// ListDatabases returns a list of all Database names.\n// TODO(kash): Include the database type (NoSQL vs. SQL).\n// TODO(sadovsky): Maybe switch to streaming RPC.",
-    inArgs: [],
-    outArgs: [{
-      name: '',
-      doc: "",
-      type: _type1
-    },
-    ],
-    inStream: null,
-    outStream: null,
-    tags: [canonicalize.reduce(new access.Tag("Read", true), new access.Tag()._type), ]
+    tags: [canonicalize.reduce(new access.Tag("Resolve", true), new access.Tag()._type), ]
   },
     
       
diff --git a/src/gen-vdl/v.io/v23/services/syncbase/nosql/index.js b/src/gen-vdl/v.io/v23/services/syncbase/nosql/index.js
index a334d66..0e25715 100644
--- a/src/gen-vdl/v.io/v23/services/syncbase/nosql/index.js
+++ b/src/gen-vdl/v.io/v23/services/syncbase/nosql/index.js
@@ -299,7 +299,7 @@
 DatabaseWatcher.prototype._serviceDescription = {
   name: 'DatabaseWatcher',
   pkgPath: 'v.io/v23/services/syncbase/nosql',
-  doc: "// DatabaseWatcher allows a client to watch for updates to the database. For\n// each watch request, the client will receive a reliable stream of watch events\n// without re-ordering. See watch.GlobWatcher for a detailed explanation of the\n// behavior.\n// TODO(rogulenko): Currently the only supported watch patterns are\n// \"<tableName>/$/<rowPrefix>*\". Consider changing that.\n//\n// The watching is done by starting a streaming RPC. The argument to the RPC\n// contains the ResumeMarker that points to a particular place in the database\n// event log. The result stream consists of a never-ending sequence of Change\n// messages (until the call fails or is canceled). Each Change contains the Name\n// field in the form \"<tableName>/$/<rowKey>\" and the Value field of the\n// StoreChange type. If the client has no access to a row specified in a change,\n// that change is excluded from the result stream.\n//\n// DatabaseWatcher is designed to be used in the following way:\n// 1) begin a read-only batch\n// 2) read all data your app needs\n// 3) read the ResumeMarker\n// 4) abort the batch\n// 5) start watching for changes to the data using the ResumeMarker\n// In this configuration the client will not miss any changes to the data.",
+  doc: "// DatabaseWatcher allows a client to watch for updates to the database. For\n// each watch request, the client will receive a reliable stream of watch events\n// without re-ordering. See watch.GlobWatcher for a detailed explanation of the\n// behavior.\n// TODO(rogulenko): Currently the only supported watch patterns are\n// \"<tableName>/<rowPrefix>*\". Consider changing that.\n//\n// The watching is done by starting a streaming RPC. The argument to the RPC\n// contains the ResumeMarker that points to a particular place in the database\n// event log. The result stream consists of a never-ending sequence of Change\n// messages (until the call fails or is canceled). Each Change contains the Name\n// field in the form \"<tableName>/<rowKey>\" and the Value field of the\n// StoreChange type. If the client has no access to a row specified in a change,\n// that change is excluded from the result stream.\n//\n// DatabaseWatcher is designed to be used in the following way:\n// 1) begin a read-only batch\n// 2) read all data your app needs\n// 3) read the ResumeMarker\n// 4) abort the batch\n// 5) start watching for changes to the data using the ResumeMarker\n// In this configuration the client will not miss any changes to the data.",
   embeds: [{
       name: 'GlobWatcher',
       pkgPath: 'v.io/v23/services/watch',
@@ -1147,7 +1147,7 @@
 Database.prototype._serviceDescription = {
   name: 'Database',
   pkgPath: 'v.io/v23/services/syncbase/nosql',
-  doc: "// Database represents a collection of Tables. Batches, queries, sync, watch,\n// etc. all operate at the Database level.\n// Database.Glob operates over Table names.\n// Param schemaVersion is the version number that the client expects the database\n// to be at. To disable schema version checking, pass -1.\n//\n// TODO(sadovsky): Add Watch method.",
+  doc: "// Database represents a collection of Tables. Batches, queries, sync, watch,\n// etc. all operate at the Database level.\n// Database.Glob operates over Table names.\n// Param schemaVersion is the version number that the client expects the\n// database to be at. To disable schema version checking, pass -1.",
   embeds: [{
       name: 'Object',
       pkgPath: 'v.io/v23/services/permissions',
@@ -1156,7 +1156,7 @@
     {
       name: 'DatabaseWatcher',
       pkgPath: 'v.io/v23/services/syncbase/nosql',
-      doc: "// DatabaseWatcher allows a client to watch for updates to the database. For\n// each watch request, the client will receive a reliable stream of watch events\n// without re-ordering. See watch.GlobWatcher for a detailed explanation of the\n// behavior.\n// TODO(rogulenko): Currently the only supported watch patterns are\n// \"<tableName>/$/<rowPrefix>*\". Consider changing that.\n//\n// The watching is done by starting a streaming RPC. The argument to the RPC\n// contains the ResumeMarker that points to a particular place in the database\n// event log. The result stream consists of a never-ending sequence of Change\n// messages (until the call fails or is canceled). Each Change contains the Name\n// field in the form \"<tableName>/$/<rowKey>\" and the Value field of the\n// StoreChange type. If the client has no access to a row specified in a change,\n// that change is excluded from the result stream.\n//\n// DatabaseWatcher is designed to be used in the following way:\n// 1) begin a read-only batch\n// 2) read all data your app needs\n// 3) read the ResumeMarker\n// 4) abort the batch\n// 5) start watching for changes to the data using the ResumeMarker\n// In this configuration the client will not miss any changes to the data."
+      doc: "// DatabaseWatcher allows a client to watch for updates to the database. For\n// each watch request, the client will receive a reliable stream of watch events\n// without re-ordering. See watch.GlobWatcher for a detailed explanation of the\n// behavior.\n// TODO(rogulenko): Currently the only supported watch patterns are\n// \"<tableName>/<rowPrefix>*\". Consider changing that.\n//\n// The watching is done by starting a streaming RPC. The argument to the RPC\n// contains the ResumeMarker that points to a particular place in the database\n// event log. The result stream consists of a never-ending sequence of Change\n// messages (until the call fails or is canceled). Each Change contains the Name\n// field in the form \"<tableName>/<rowKey>\" and the Value field of the\n// StoreChange type. If the client has no access to a row specified in a change,\n// that change is excluded from the result stream.\n//\n// DatabaseWatcher is designed to be used in the following way:\n// 1) begin a read-only batch\n// 2) read all data your app needs\n// 3) read the ResumeMarker\n// 4) abort the batch\n// 5) start watching for changes to the data using the ResumeMarker\n// In this configuration the client will not miss any changes to the data."
     },
     {
       name: 'SyncGroupManager',
@@ -1236,13 +1236,13 @@
     ],
     inStream: null,
     outStream: null,
-    tags: [canonicalize.reduce(new access.Tag("Read", true), new access.Tag()._type), ]
+    tags: [canonicalize.reduce(new access.Tag("Resolve", true), new access.Tag()._type), ]
   },
     
       
     {
     name: 'ListTables',
-    doc: "// ListTables returns a list of all Table names.\n// TODO(sadovsky): Maybe switch to streaming RPC.",
+    doc: "// ListTables returns a list of all Table names.\n// This method exists on Database but not on Service or App because for the\n// latter we can simply use glob, while for the former glob fails on\n// BatchDatabase since we encode the batch id in the BatchDatabase object\n// name. More specifically, the glob client library appears to have two odd\n// behaviors:\n// 1) It checks Resolve access on every component along the path (by doing a\n//    Dispatcher.Lookup), whereas this doesn't happen for other RPCs.\n// 2) It does a Glob(<prefix>/*) for every prefix path, and only proceeds to\n//    the next path component if that component appeared in its parent's Glob\n//    results. This is inefficient in general, and broken for us since\n//    Glob(\"app/*\") does not return batch database names like \"a/d##bId\".\n// TODO(sadovsky): Maybe switch to streaming RPC.",
     inArgs: [],
     outArgs: [{
       name: '',
@@ -1283,7 +1283,7 @@
       
     {
     name: 'BeginBatch',
-    doc: "// BeginBatch creates a new batch. It returns an App-relative name for a\n// Database handle bound to this batch. If this Database is already bound to a\n// batch, BeginBatch() will fail with ErrBoundToBatch. Concurrency semantics\n// are documented in model.go.\n// TODO(sadovsky): Maybe make BatchOptions optional.",
+    doc: "// BeginBatch creates a new batch. It returns a \"batch suffix\" string to\n// append to the object name of this Database, yielding an object name for the\n// Database bound to the created batch. (For example, if this Database is\n// named \"/path/to/db\" and BeginBatch returns \"##abc\", the client should\n// construct batch Database object name \"/path/to/db##abc\".) If this Database\n// is already bound to a batch, BeginBatch() will fail with ErrBoundToBatch.\n// Concurrency semantics are documented in model.go.\n// TODO(sadovsky): Maybe make BatchOptions optional.",
     inArgs: [{
       name: 'schemaVersion',
       doc: "",
@@ -1976,7 +1976,7 @@
     ],
     inStream: null,
     outStream: null,
-    tags: [canonicalize.reduce(new access.Tag("Read", true), new access.Tag()._type), ]
+    tags: [canonicalize.reduce(new access.Tag("Resolve", true), new access.Tag()._type), ]
   },
     
       
@@ -2191,7 +2191,7 @@
       
     {
     name: 'Exists',
-    doc: "// Exists returns true only if this Row exists. Insufficient permissions\n// cause Exists to return false instead of an error.\n// TODO(ivanpi): Exists may fail with an error if higher levels of hierarchy\n// do not exist.",
+    doc: "// Exists returns true only if this Row exists. Insufficient permissions\n// cause Exists to return false instead of an error.\n// Note, Exists on Row requires read permissions, unlike higher levels of\n// hierarchy which require resolve, because Row existence usually carries\n// more information.\n// TODO(ivanpi): Exists may fail with an error if higher levels of hierarchy\n// do not exist.",
     inArgs: [{
       name: 'schemaVersion',
       doc: "",
diff --git a/src/nosql/database.js b/src/nosql/database.js
index aef4454..4438831 100644
--- a/src/nosql/database.js
+++ b/src/nosql/database.js
@@ -185,7 +185,9 @@
  * @param {function} cb Callback.
  */
 Database.prototype.listTables = function(ctx, cb) {
-  util.listChildren(ctx, this.fullName, cb);
+  // See comment in v.io/v23/services/syncbase/nosql/service.vdl for why we
+  // can't implement listTables using Glob (via util.listChildren).
+  this._wire(ctx).listTables(ctx, cb);
 };
 
 /**
diff --git a/src/util.js b/src/util.js
index 0a1e52b..56aed63 100644
--- a/src/util.js
+++ b/src/util.js
@@ -137,9 +137,7 @@
  * @return {string} Escaped string.
  */
 function escape(s) {
-  return s
-    .replace(/%/g, '%25')
-    .replace(/\//g, '%2F');
+  return vanadium.naming.encodeAsNameElement(s);
 }
 
 /**
@@ -149,5 +147,5 @@
  * @return {string} Unescaped string.
  */
 function unescape(s) {
-  return decodeURIComponent(s);
+  return vanadium.naming.decodeFromNameElement(s);
 }
diff --git a/test/integration/test-app.js b/test/integration/test-app.js
index ef2a68e..f7e5860 100644
--- a/test/integration/test-app.js
+++ b/test/integration/test-app.js
@@ -49,7 +49,9 @@
     var appNames = [
       uniqueName('app'),
       uniqueName('app'),
-      uniqueName('app')
+      uniqueName('app'),
+      uniqueName('app/b'),  // symbols are okay
+      uniqueName('dev.v.io/a/admin@myapp.com'),
     ];
 
     async.waterfall([
@@ -58,7 +60,7 @@
         o.service.app(appName).exists(o.ctx, cb);
       }),
       function(existsArray, cb) {
-        t.deepEqual(existsArray, [false, false, false],
+        t.deepEqual(existsArray, [false, false, false, false, false],
           'exists: no apps exist');
         cb(null);
       },
@@ -81,7 +83,7 @@
         o.service.app(appName).exists(o.ctx, cb);
       }),
       function(existsArray, cb) {
-        t.deepEqual(existsArray, [true, true, true],
+        t.deepEqual(existsArray, [true, true, true, true, true],
           'exists: all apps exist');
         cb(null);
       },
diff --git a/test/integration/test-table.js b/test/integration/test-table.js
index c82ede0..6e97b2a 100644
--- a/test/integration/test-table.js
+++ b/test/integration/test-table.js
@@ -16,7 +16,7 @@
 // TODO(aghassemi): We fail to bind to Unicode names; investigate.
 //var ROW_KEY = 'چשKEYઑᜰ';
 //var ROW_VAL = '⛓⛸VALϦӪ';
-var ROW_KEY = 'row_key';
+var ROW_KEY = 'row_key/a^b';  // symbols are okay
 var ROW_VAL = 'row value';
 
 test('table.create() creates a table', function(t) {
@@ -346,8 +346,7 @@
   });
 }
 
-//TODO(aghassemi) Skipped test.
-//Set permission for prefix != "" is not implemented.
+// TODO(aghassemi) Enable this test.
 test.skip('Getting/Setting permissions on rows', function(t) {
   setupTable(t, function(err, o) {
     if (err) {