Author: mhermanto
Date: Tue Sep 21 00:57:03 2010
New Revision: 999173

URL: http://svn.apache.org/viewvc?rev=999173&view=rev
Log:
Common container updates:
- Push down caching mechanism to service for gadget metadata.
- Now builds full response from partial request/response.
- Allow defaultable view (ie: if requested view does not exist, fallback to 
view=default).
- More friendly runtime error checking.
- Per-gadget error reporting.

http://codereview.appspot.com/2226041/

Modified:
    shindig/trunk/features/src/main/javascript/features/container/constant.js
    shindig/trunk/features/src/main/javascript/features/container/container.js
    
shindig/trunk/features/src/main/javascript/features/container/gadget_holder.js
    shindig/trunk/features/src/main/javascript/features/container/gadget_site.js
    shindig/trunk/features/src/main/javascript/features/container/service.js
    shindig/trunk/features/src/main/javascript/features/container/util.js

Modified: 
shindig/trunk/features/src/main/javascript/features/container/constant.js
URL: 
http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/constant.js?rev=999173&r1=999172&r2=999173&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container/constant.js 
(original)
+++ shindig/trunk/features/src/main/javascript/features/container/constant.js 
Tue Sep 21 00:57:03 2010
@@ -58,6 +58,7 @@ shindig.container.TokenResponse.TOKEN = 
  * @enum {string}
  */
 shindig.container.RenderParam = {};
+shindig.container.RenderParam.ALLOW_DEFAULT_VIEW = 'allowDefaultView';
 shindig.container.RenderParam.CLASS = 'class';
 shindig.container.RenderParam.DEBUG = 'debug';
 shindig.container.RenderParam.HEIGHT = 'height';
@@ -67,6 +68,7 @@ shindig.container.RenderParam.USER_PREFS
 shindig.container.RenderParam.VIEW = 'view';
 shindig.container.RenderParam.WIDTH = 'width';
 
+
 /**
  * Constants to key into request viewParam JSON.
  * @enum {string}

Modified: 
shindig/trunk/features/src/main/javascript/features/container/container.js
URL: 
http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/container.js?rev=999173&r1=999172&r2=999173&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container/container.js 
(original)
+++ shindig/trunk/features/src/main/javascript/features/container/container.js 
Tue Sep 21 00:57:03 2010
@@ -44,6 +44,13 @@ shindig.container.Container = function(o
   this.sites_ = {};
 
   /**
+   * @type {boolean}
+   */
+  this.allowDefaultView_ = Boolean(
+      shindig.container.util.getSafeJsonValue(config,
+      shindig.container.ContainerConfig.ALLOW_DEFAULT_VIEW, true));
+
+  /**
    * @type {string}
    * @private
    */
@@ -152,12 +159,15 @@ shindig.container.Container.prototype.ge
 shindig.container.Container.prototype.navigateGadget = function(
     site, gadgetUrl, viewParams, renderParams, opt_callback) {
   var callback = opt_callback || function() {};
+  if (this.allowDefaultView_) {
+    renderParams[shindig.container.RenderParam.ALLOW_DEFAULT_VIEW] = true;
+  }
   if (this.renderDebug_) {
-    renderParams['nocache'] = true;
-    renderParams['debug'] = true;
+    renderParams[shindig.container.RenderParam.NO_CACHE] = true;
+    renderParams[shindig.container.RenderParam.DEBUG] = true;
   }
   if (this.renderTest_) {
-    renderParams['testmode'] = true;
+    renderParams[shindig.container.RenderParam.TEST_MODE] = true;
   }
 
   var self = this;
@@ -166,6 +176,10 @@ shindig.container.Container.prototype.na
     // TODO: Navigate to error screen on primary gadget load failure
     // TODO: Should display error without doing a standard navigate.
     // TODO: Bad if the error gadget fails to load.
+    if (gadgetInfo.error) {
+      throw [ 'Failed to possibly schedule token refresh for gadget ',
+          holder.getUrl(), '.' ].join('');
+    }
     if (gadgetInfo[shindig.container.MetadataResponse.NEEDS_TOKEN_REFRESH]) {
       self.scheduleRefreshTokens_();
     }
@@ -205,13 +219,14 @@ shindig.container.Container.prototype.pr
   var request = shindig.container.util.newMetadataRequest(gadgetUrls);
   var self = this;
   this.service_.getGadgetMetadata(request, function(response) {
-    if (!response.error) {
-      for (var id in response) {
-        self.addPreloadedGadgetUrl_(id);
-        if 
(response[id][shindig.container.MetadatResponse.NEEDS_TOKEN_REFRESH]) {
-          // Safe to re-schedule many times.
-          self.scheduleRefreshTokens_();
-        }
+    for (var id in response) {
+      if (response[id].error) {
+        throw [ 'Failed to preload gadget ', id, '.' ].join('');
+      }
+      self.addPreloadedGadgetUrl_(id);
+      if (response[id][shindig.container.MetadatResponse.NEEDS_TOKEN_REFRESH]) 
{
+        // Safe to re-schedule many times.
+        self.scheduleRefreshTokens_();
       }
     }
   });
@@ -285,6 +300,12 @@ shindig.container.ContainerConfig.TOKEN_
  */
 shindig.container.ContainerRender = {};
 /**
+ * Allow gadgets to render in unspecified view.
+ * @type {string}
+ * @const
+ */
+shindig.container.ContainerRender.ALLOW_DEFAULT_VIEW = 'allowDefaultView';
+/**
  * Style class to associate to iframe.
  * @type {string}
  * @const
@@ -443,17 +464,19 @@ shindig.container.Container.prototype.re
 
   var self = this;
   this.service_.getGadgetToken(request, function(response) {
-    if (!response.error) {
-      // Update active token-requiring gadgets with new tokens. Do not need to
-      // update pre-loaded gadgets, since new tokens will take effect when they
-      // are navigated to, from cache.
-      for (var key in self.sites_) {
-        var holder = self.sites_[key].getActiveGadgetHolder();
-        var gadgetInfo = 
self.service_.getCachedGadgetMetadata(holder.getUrl());
-        if 
(gadgetInfo[shindig.container.MetadataResponse.NEEDS_TOKEN_REFRESH]) {
-          gadgets.rpc.call(holder.getIframeId(), 'update_security_token', null,
-              
response[holder.getUrl()][shindig.container.TokenResponse.TOKEN]);
+    // Update active token-requiring gadgets with new tokens. Do not need to
+    // update pre-loaded gadgets, since new tokens will take effect when they
+    // are navigated to, from cache.
+    for (var key in self.sites_) {
+      var holder = self.sites_[key].getActiveGadgetHolder();
+      var gadgetInfo = self.service_.getCachedGadgetMetadata(holder.getUrl());
+      if (gadgetInfo[shindig.container.MetadataResponse.NEEDS_TOKEN_REFRESH]) {
+        var tokenInfo = response[holder.getUrl()];
+        if (tokenInfo.error) {
+          throw [ 'Failed to get token for gadget ', holder.getUrl(), '.' 
].join('');
         }
+        gadgets.rpc.call(holder.getIframeId(), 'update_security_token', null,
+            tokenInfo[shindig.container.TokenResponse.TOKEN]);
       }
     }
     // TODO: Tokens will be stale, but error should not be ignored.

Modified: 
shindig/trunk/features/src/main/javascript/features/container/gadget_holder.js
URL: 
http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/gadget_holder.js?rev=999173&r1=999172&r2=999173&view=diff
==============================================================================
--- 
shindig/trunk/features/src/main/javascript/features/container/gadget_holder.js 
(original)
+++ 
shindig/trunk/features/src/main/javascript/features/container/gadget_holder.js 
Tue Sep 21 00:57:03 2010
@@ -159,9 +159,8 @@ shindig.container.GadgetHolder.prototype
 /**
  * Render a gadget into the element.
  * @param {Object} gadgetInfo the JSON gadget description.
- * @param {Object} viewParams View parameters for the gadget.
- * @param {Object} renderParams Render parameters for the gadget, including:
- *     view, width, height.
+ * @param {Object} viewParams Look at shindig.container.ViewParam.
+ * @param {Object} renderParams. Look at shindig.container.RenderParam.
  */
 shindig.container.GadgetHolder.prototype.render = function(
     gadgetInfo, viewParams, renderParams) {
@@ -170,10 +169,6 @@ shindig.container.GadgetHolder.prototype
   this.gadgetInfo_ = gadgetInfo;
   this.viewParams_ = viewParams;
   this.renderParams_ = renderParams;
-  if 
(!this.gadgetInfo_[shindig.container.MetadataResponse.VIEWS][this.getView()]) {
-    throw 'View ' + this.view_ + ' unsupported in ' + this.gadgetInfo_['url'];
-  }
-
   this.el_.innerHTML = this.getIframeHtml_();
 
   // Set up RPC channel. RPC relay url is on gmodules, relative to base of the
@@ -266,7 +261,7 @@ shindig.container.GadgetHolder.prototype
 
   uri.setFP('mid', String(this.siteId_));
 
-  if (this.hasViewParams_()) {
+  if (!shindig.container.util.isEmptyJson(this.viewParams_)) {
     var gadgetParamText = gadgets.json.stringify(this.viewParams_);
     uri.setFP('view-params', gadgetParamText);
   }
@@ -297,16 +292,3 @@ shindig.container.GadgetHolder.prototype
     }
   }
 };
-
-
-/**
- * Return true if this has view parameters.
- * @private
- * @return {Boolean}
- */
-shindig.container.GadgetHolder.prototype.hasViewParams_ = function() {
-  for (var key in this.viewParams_) {
-    return true;
-  }
-  return false;
-};

Modified: 
shindig/trunk/features/src/main/javascript/features/container/gadget_site.js
URL: 
http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/gadget_site.js?rev=999173&r1=999172&r2=999173&view=diff
==============================================================================
--- 
shindig/trunk/features/src/main/javascript/features/container/gadget_site.js 
(original)
+++ 
shindig/trunk/features/src/main/javascript/features/container/gadget_site.js 
Tue Sep 21 00:57:03 2010
@@ -194,45 +194,35 @@ shindig.container.GadgetSite.prototype.g
 /**
  * Render a gadget in the site, by URI of the gadget XML.
  * @param {string} gadgetUrl The absolute URL to gadget.
- * @param {Object} viewParams View parameters for the gadget.
- * @param {Object} renderParams. Render parameters for the gadget, including:
- *     view, width, height.
- * @param {function(Object)=} opt_callback Function called with gadget info 
after
- *     navigation has occurred.
+ * @param {Object} viewParams Look at shindig.container.ViewParam.
+ * @param {Object} renderParams. Look at shindig.container.RenderParam.
+ * @param {function(Object)=} opt_callback Function called with gadget info
+ *     after navigation has occurred.
  */
 shindig.container.GadgetSite.prototype.navigateTo = function(
     gadgetUrl, viewParams, renderParams, opt_callback) {
   var callback = opt_callback || function() {};
-
-  // If metadata has been loaded/cached.
-  var gadgetInfo = this.service_.getCachedGadgetMetadata(gadgetUrl);
-  if (gadgetInfo) {
-    this.render(gadgetInfo, viewParams, renderParams);
+  var request = shindig.container.util.newMetadataRequest([gadgetUrl]);
+  var self = this;
+  this.service_.getGadgetMetadata(request, function(response) {
+    var gadgetInfo = response[gadgetUrl];
+    if (gadgetInfo.error) {
+      var message = [ 'Failed to navigate for gadget ', gadgetUrl, '.' 
].join(''); 
+      shindig.container.util.warn(message);
+    } else {
+      self.render(gadgetInfo, viewParams, renderParams);
+    }
+    // Possibly with an error. Leave to user to deal with raw response.
     callback(gadgetInfo);
-
-  // Otherwise, fetch gadget metadata.
-  } else {
-    var request = shindig.container.util.newMetadataRequest([gadgetUrl]);
-    var self = this;
-    this.service_.getGadgetMetadata(request, function(response) {
-      if (!response.error) {
-        var gadgetInfo = response[gadgetUrl];
-        self.render(gadgetInfo, viewParams, renderParams);
-        callback(gadgetInfo);
-      } else {
-        callback(response);
-      }
-    });
-  }
+  });
 };
 
 
 /**
  * Render a gadget in this site, using a JSON gadget description.
  * @param {Object} gadgetInfo the JSON gadget description.
- * @param {Object} viewParams View parameters for the gadget.
- * @param {Object} renderParams. Render parameters for the gadget, including:
- *     view, width, height.
+ * @param {Object} viewParams Look at shindig.container.ViewParam.
+ * @param {Object} renderParams. Look at shindig.container.RenderParam.
  */
 shindig.container.GadgetSite.prototype.render = function(
     gadgetInfo, viewParams, renderParams) {
@@ -243,16 +233,24 @@ shindig.container.GadgetSite.prototype.r
     previousView = this.currentGadgetHolder_.getView();
   }
 
-  // Load into the double-buffer if there is one
+  // Load into the double-buffer if there is one.
   var el = this.loadingGadgetEl_ || this.currentGadgetEl_;
   this.loadingGadgetHolder_ = new shindig.container.GadgetHolder(this.id_, el);
 
+  // Find requested view.
   var view = renderParams[shindig.container.RenderParam.VIEW]
       || viewParams[shindig.container.ViewParam.VIEW]
-      || previousView
-      || 'default';
+      || previousView;
   var viewInfo = gadgetInfo[shindig.container.MetadataResponse.VIEWS][view];
 
+  if (!viewInfo && 
renderParams[shindig.container.RenderParam.ALLOW_DEFAULT_VIEW]) {
+    view = shindig.container.GadgetSite.DEFAULT_VIEW_;
+    viewInfo = gadgetInfo[shindig.container.MetadataResponse.VIEWS][view];
+  }
+  if (!viewInfo) {
+    throw [ 'Unsupported view ', view, ' for gadget ', gadgetInfo_['url'], '.' 
].join('');
+  }
+
   var delayLoad = this.getFeature('loadstate', gadgetInfo) ||
       this.getFeature('shell', gadgetInfo);
 
@@ -311,8 +309,7 @@ shindig.container.GadgetSite.prototype.r
  * If token has been fetched at least once, set the token to the most recent
  * one. Otherwise, leave it.
  * @param {Object} gadgetInfo The gadgetInfo used to update security token.
- * @param {Object} renderParams Render parameters for the gadget, including:
- *     view, width, and height.
+ * @param {Object} renderParams. Look at shindig.container.RenderParam.
  */
 shindig.container.GadgetSite.prototype.updateSecurityToken_
     = function(gadgetInfo, renderParams) {
@@ -429,3 +426,11 @@ shindig.container.GadgetSite.DEFAULT_HEI
  * @private
  */
 shindig.container.GadgetSite.DEFAULT_WIDTH_ = 320;
+
+
+/**
+ * Default view of gadget.
+ * @type {string}
+ * @private
+ */
+shindig.container.GadgetSite.DEFAULT_VIEW_ = 'default';

Modified: 
shindig/trunk/features/src/main/javascript/features/container/service.js
URL: 
http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/service.js?rev=999173&r1=999172&r2=999173&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container/service.js 
(original)
+++ shindig/trunk/features/src/main/javascript/features/container/service.js 
Tue Sep 21 00:57:03 2010
@@ -45,16 +45,6 @@ shindig.container.Service = function(opt
       shindig.container.ServiceConfig.API_PATH, '/api/rpc/cs'));
 
   /**
-<<<<<<< HEAD
-=======
-   * @type {boolean}
-   * @private
-   */
-  this.sameDomain_ = Boolean(shindig.container.util.getSafeJsonValue(config,
-      shindig.container.ServiceConfig.SAME_DOMAIN, false));
-
-  /**
->>>>>>> more gjslint cleanups
    * Map of gadget URLs to cached gadgetInfo response.
    * @type {Object}
    * @private
@@ -83,31 +73,52 @@ shindig.container.Service.prototype.onCo
 
 
 /**
- * Do an immediate fetch of gadgets metadata for gadgets in request.ids, for
- * container request.container. The optional callback opt_callback will be
+ * Return a possibly-cached gadgets metadata for gadgets in request.ids, for
+ * container request.container. If metadata is not cache, fetch from server
+ * only for the uncached gadget URLs. The optional callback opt_callback will 
be
  * called, after a response is received.
  * @param {Object} request JSON object representing the request.
  * @param {function(Object)=} opt_callback function to call upon data receive.
  */
 shindig.container.Service.prototype.getGadgetMetadata = function(
     request, opt_callback) {
+  // TODO: come up with an expiration mechanism to evict cached gadgets.
+  // Can be based on renderParam['nocache']. Be careful with preloaded and
+  // arbitrarily-navigated gadgets. The former should be indefinite, unless
+  // unloaded. The later can done without user knowing.
   var callback = opt_callback || function() {};
-  var self = this;
-  osapi.gadgets.metadata.get(request).execute(function(response) {
-    if (response.error) {
-      // Hides internal server error.
-      callback({
-        error: 'Failed to retrieve gadget metadata.',
-        errorCode: 'NOLOAD'
-      });
-    } else {
-      for (var id in response) {
-        var gadgetInfo = response[id];
-        self.cachedMetadatas_[id] = gadgetInfo;
+  var uncachedUrls = this.getUncachedUrls_(request, this.cachedMetadatas_);
+  var finalResponse = this.getCachedData_(request, this.cachedMetadatas_);
+
+  // If fully cached, return from cache.
+  if (uncachedUrls.length == 0) {
+    callback(finalResponse);
+
+  // Otherwise, request for uncached metadatas.
+  } else {
+    var self = this;
+    request = shindig.container.util.newMetadataRequest(uncachedUrls);
+    osapi.gadgets.metadata.get(request).execute(function(response) {
+
+      // If response entirely fails, augment individual errors.
+      if (response.error) {
+        for (var i = 0; i < request.ids.length; i++) {
+          var id = request.ids[i];
+          var message = [ 'Server failure to fetch metadata for gadget ', id, 
'.' ].join('');
+          finalResponse[id] = { error : message };
+        }
+
+      // Otherwise, cache response. Augment final response with server 
response.
+      } else {
+        for (var id in response) {
+          self.cachedMetadatas_[id] = response[id];
+          finalResponse[id] = response[id];
+        }
       }
-      callback(response);
-    }
-  });
+
+      callback(finalResponse);
+    });
+  }
 };
 
 
@@ -118,20 +129,29 @@ shindig.container.Service.prototype.getG
 shindig.container.Service.prototype.getGadgetToken = function(
     request, opt_callback) {
   var callback = opt_callback || function() {};
+
+  // Do not check against cache. Always do a server fetch.
   var self = this;
   osapi.gadgets.token.get(request).execute(function(response) {
+    var finalResponse = {};
+
+    // If response entirely fails, augment individual errors.
     if (response.error) {
-      // Hides internal server error.
-      callback({
-        error: 'Failed to retrieve gadget token.',
-        errorCode: 'NOLOAD'
-      });
+      for (var i = 0; i < request.ids.length; i++) {
+        var id = request.ids[i];
+        var message = [ 'Server failure to fetch token for gadget ' + id + '.' 
].join('');
+        finalResponse[id] = { error : message };
+      }
+
+    // Otherwise, cache response. Augment final response with server response.
     } else {
       for (var id in response) {
         self.cachedTokens_[id] = response[id];
+        finalResponse[id] = response[id];
       }
-      callback(response);
     }
+
+    callback(finalResponse);
   });
 };
 
@@ -175,6 +195,44 @@ shindig.container.Service.prototype.init
 };
 
 
+/**
+ * Filter cache with requested ids.
+ * @param {Object} request containing ids.
+ * @param {Object} cache JSON containing cached data.
+ * @return {Object} JSON containing requested and cached entries.
+ * @private
+ */
+shindig.container.Service.prototype.getCachedData_ = function(request, cache) {
+  var result = {};
+  for (var i = 0; i < request.ids.length; i++) {
+    var id = request.ids[i];
+    if (cache[id]) {
+      result[id] = cache[id];
+    }
+  }
+  return result;
+};
+
+
+/**
+ * Extract ids in request not in cache.
+ * @param {Object} request containing ids.
+ * @param {Object} cache JSON containing cached data.
+ * @return {Array.<string>} keys in the json.
+ * @private
+ */
+shindig.container.Service.prototype.getUncachedUrls_ = function(request, 
cache) {
+  var result = [];
+  for (var i = 0; i < request.ids.length; i++) {
+    var id = request.ids[i];
+    if (!cache[id]) {
+      result.push(id);
+    }
+  }
+  return result;
+};
+
+
 // 
-----------------------------------------------------------------------------
 // Configuration
 // 
-----------------------------------------------------------------------------
@@ -186,6 +244,7 @@ shindig.container.Service.prototype.init
  * @enum {string}
  */
 shindig.container.ServiceConfig = {};
+
 /**
  * Host to fetch gadget information, via XHR.
  * @type {string}

Modified: shindig/trunk/features/src/main/javascript/features/container/util.js
URL: 
http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/util.js?rev=999173&r1=999172&r2=999173&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container/util.js 
(original)
+++ shindig/trunk/features/src/main/javascript/features/container/util.js Tue 
Sep 21 00:57:03 2010
@@ -59,10 +59,6 @@ shindig.container.util.mergeJsons = func
 };
 
 
-shindig.container.util.cloneJson = function(json) {
-       
-};
-
 /**
  * Construct a JSON request to get gadget metadata. For now, this will request
  * a super-set of data needed for all CC APIs requiring gadget metadata, since
@@ -112,3 +108,27 @@ shindig.container.util.toArrayOfJsonKeys
   }
   return result;
 };
+
+
+/**
+ * Return true if json is empty.
+ * @param {Object} json to check.
+ * @return {Boolean}
+ */
+shindig.container.util.isEmptyJson = function(json) {
+  for (var key in json) {
+    return false;
+  }
+  return true;
+};
+
+
+/**
+ * Put up a warning message to console.
+ * @param {String} message to warn with.
+ */
+shindig.container.util.warn = function(message) {
+  if (console && console.warn) {
+    console.warn(message);
+  }
+}


Reply via email to