With a system with many moving parts, we need a way to see progress and to control the behavior as we go. For that I’m using 

  • A google sheet to display results so far
  • A google sheet to provide parameters
  • An Apps Script add on to subscribe to changes and update the cache
  • An Apps Script add on to be woken up when something changes.

The detail and code for all of this can be found in this add-on

The parameters

These specify the area for which we are generating a pub crawl

Each row represents an area, the crawl size is how many bars to include in a crawl, and suggestion time is how often to ask for a bar suggestion. The places-type is used by the cloud function simulating suggesting bars (the suggestor) to know what kind of place to look for. For every row, there is supporting data describing the area in terms of a group of lat/lng co-ords making a polygon which encloses and area. It’s a simple list – a snippet of below.

The web app

A web app is published which listens for changes in cache. Each pub crawl is given a name and the object of the Apps Script web app is to declare a pub crawl when it has received enough suggestions from the suggestor, and to display the contents of cache in a sheet. The number of pub crawls declared is limited to the number in this list. When a pub crawl is declared, a time stamp is added to this list and it can’t be used again until a crawl space is freed up.

Progress is reported by the webapp in this sheet

The addon

Here you can select the area to work on, restart or pause the demo, clear the sheet and watch the summary of suggestions and pub crawl declarations.  It detects changes to the cache by subscribing to it.

The code

I won’t go through all the code here as it’s mainly regular apps script stuff, but I’ll just highlight how interactions with the effex cache happen.
Rendering changes in the add-on
The add-on keeps up to date by subscribing to changes in cache using the Efx JavaScript client

 // set up a push watch for this client, that will update the display
      var pushOn = ns.settings.efx.on("update", keys.item, keys.updater, 
        function(id,packet){
          ns.render();
        }, {type:"push"});

On notification of a change, it reads the item from cache and renders the latest values.

 /**
   * render an update - called when something changes
   */
  ns.render = function () {
  
    var keys = ns.settings.efx.getKeys();
    ns.settings.data = null;
    
    ns.settings.efx.read (keys.item , keys.reader ) 
    .then (function (result) {
      if (!result.data.ok) {
        App.showNotification ("Error getting item", JSON.stringify(result.data));
        return null;
      }
      else {
        ns.settings.data = result.data;
        return Report.render(result.data.value);
      }
    })
  };

Rendering changes in the webapp

The webapp is invoked by detecting a doPost call – but how does it get that call?  Actually, the client-side JavaScript efx client is used to listen to changes twice. Once to render in the add-on – using a push notification received through WebSockets , as above, but also subscribes to a URL notification on behalf of the server-side webapp. 

var urlOn = ns.settings.efx.on("update", keys.item, keys.updater, data.value.keys['pd-control-link'], {
        type:"url",
        message:{updater:keys.updater, ssid:keys.ssid}
      });

The published webapp url makes part of the parameters and has automatically been populated with the item key for the cache as a parameter.

and, for security, the access key is passed as a message in the post body.  Now every time a change is detected the efx api client will

  • call ns.render on the client
  • issue a post request to the webapp

Over in the web app, the doPost function is expecting to hear from the cache from time to time, but it wants to ignore any changes that it generated itself (since the webapp is able to update cache when a new crawl is declared). The cache contains a session property which can be used to flag who last wrote to it. The webapp uses its own url to set its session, so when an update is detected, and the session matches it owns – it knows to ignore it. When it accesses the cache it takes out an intention to update – this locks that particular cache item from being updated by anyone else in the meantime. If no action is required it releases the intent and exits, otherwise it will proceed with updating the sheet .. which I won’t go into here. This time, we’re using the Apps Script client to efx.

// set session to this webapp - we can use that to avoid processing our own updates
    efx.setSession(ScriptApp.getService().getUrl());

    // get the item we've been woken up about, and hold it for update
    var result = efx.read(post.id, post.message.updater, {
      intention: "update",
      backoff: true
    });
    var data = result.data;

    if (!data.ok) throw 'failed to get item in url notification ' + JSON.stringify(data);
        
    if (efx.getSession() === data.session) {
      response.code = 202;
      response.error = "ignored";
      var r = efx.release(data.id, data.reader, data.intent)
      if (!r.data.ok) console.log('failed to release intent ' + JSON.stringify(r.data));
      return null;
    }

If it has made any changes, then it needs to write back to cache, using the intent key is previously took out to show its intention to update and the updater keys that was passed to the post as a message.

 // update the item if necessary
    if (isDeclared) {
      var result = efx.update(data.value, data.id, post.message.updater, "post", {
        intent: data.intent
      });
      var data = result.data;
      if (!data.ok) throw 'failed to update efx ' + JSON.stringify(data);
    }

You can read the api documentation for efx here 

For more like this, see Ephemeral Exchange

Why not join our forum, follow the blog or follow me on Twitter to ensure you get updates when they are available.