Orkut Application Platform

On-Site Application Developer Guide: App Data

Contents

What is AppData?

Orkut allows your application to save small amounts of data associated to a particular person. There are API calls that allow you to store and retrieve named variables, so it essentially works like a key/value map or dictionary. For example, to save the value "pizza" under the key "favfood", you would issue an OpenSocial UpdatePersonAppDataRequest as illustrated below:

var idspec = opensocial.IdSpec.PersonId.VIEWER;
var req = opensocial.newDataRequest();
req.add(req.newUpdatePersonAppDataRequest(
       idspec_string, "favfood", "pizza"));
req.send(onDataSaved);

To read that data back, you can issue a FetchPersonAppDataRequest:

var idspec_string = opensocial.IdSpec.PersonId.VIEWER;
var idspec_object = opensocial.newIdSpec( { 'userId': 'VIEWER' } );

var req = opensocial.newDataRequest();
var fields = [ "favfood" ]; // the fields we want to read

req.add(req.newFetchPersonRequest(idspec_string), "viewer");
req.add(req.newFetchPersonAppDataRequest(idspec_object, 
       fields), "appdata");
req.send(onDataReceived);

A few things might be surprising about this snippet, so let's briefly comment it. First of all, notice that we are not only requesting the data, but also the viewer information. This is necessary because when we receive a response, we will need the viewer's OpenSocial ID to retrieve the actual data (this will become clearer when you see the code for onDataReceived).

The second potentially surprising aspect of the code above is that we have to specify the IdSpec in two different ways: as a string or as an object, depending on the request we are making. The FetchPersonRequest takes the IdSpec as a string, while the FetchPersonAppDataRequest takes an IdSpec object. This is a minor implementation quirk you have to be aware of, since passing the incorrect IdSpec type will cause the requests to fail.

Receiving the Data

When your callback is invoked, you have to retrieve the data that you asked for. This process can be a little complicated at first glance, so let's take a closer look at an example:

function onDataReceived(data) {
   if (data.hadError()) {
      debug.say("*** ERROR: " + data.getErrorMessage());
      // handle error here
      return;
   }

   var viewer = data.get('viewer').getData();
   var appdata = data.get('appdata').getData();

   if (typeof appdata == 'undefined' || appdata == null) {
      debug.say("No data to load.");
      return;
   }

   // appdata is a dictionary keyed by OpenSocial ID
   viewerData = appdata[viewer.getId()];

   if (!viewerData) {
      debug.say("No data to load.");
      return;
   }

   var favcolor = viewerData["favcolor"];
   var favfood  = viewerData["favfood"];

   // do something with favcolor and favfood here (render it
   // on your UI, for example)

   debug.say("Loaded successfully: " + favcolor + "," + favfood);
}

As you can see, the data object you get contains two data pieces: the viewer and the appdata. This is because we requested these two pieces in our previous code snippet. Next, we have to test if the data is really available. The most obvious scenario when this happens is, for example, if your application hasn't saved that data yet. Next, we have to retrieve the dictionary of values. Surprisingly enough, this is not appdata. Rather, appdata is a dictionary that maps an OpenSocial ID to a dictionary of appdata. So we have to retrieve the viewer's OpenSocial ID via viewer.getId() to index the appdata dictionary and get the actual dictionary with the fields (viewerData). From that point on, it's straightforward.

A Full Example

Now let's see a fully functional example. The application below can save and load two pieces of data (favorite food and favorite color) associated with the viewer, and uses a simple HTML interface to display them:

<table>
   <tr>
      <td>Favorite color:</td>
      <td><input type='text' id='favcolor'></td>
   </tr>
   <tr>
      <td>Favorite food:</td>
      <td><input type='text' id='favfood'></td>
   </tr>
</table>
<button id='loadbutton'>Load</button>  
<button id='savebutton'>Save</button>  
<button id='delbutton'>Delete</button><br>
<span id='status'></span>

<script type='text/javascript'>
var txt_favcolor, txt_favfood, span_status;
var idspec_string = opensocial.IdSpec.PersonId.VIEWER;
var idspec_object = opensocial.newIdSpec( { 'userId': 'VIEWER' } );

function init() {
   txt_favcolor = document.getElementById('favcolor');
   txt_favfood  = document.getElementById('favfood');
   span_status  = document.getElementById('status');

   // attach event listener to the buttons
   document.getElementById('loadbutton').addEventListener('click', loadPrefs, false);
   document.getElementById('savebutton').addEventListener('click', savePrefs, false);
   document.getElementById('delbutton').addEventListener('click', delPrefs, false);
}

function setStatus(s) {
   span_status.innerHTML = s;
   debug.say(s);
}

function loadPrefs() {
   var req = opensocial.newDataRequest();
   var fields = [ "favcolor", "favfood" ];

   req.add(req.newFetchPersonRequest(idspec_string), "viewer");
   req.add(req.newFetchPersonAppDataRequest(idspec_object, 
       fields), "appdata");
   req.send(onDataReceived);
   setStatus("Request sent. Waiting for reply...");
}

function onDataReceived(data) {
   if (data.hadError()) {
      setStatus("*** ERROR: " + data.getErrorMessage());
      // handle error here
      return;
   }

   var viewer = data.get('viewer').getData();
   var appdata = data.get('appdata').getData();

   if (typeof appdata == 'undefined' || appdata == null) {
      setStatus("No data to load.");
      return;
   }

   // appdata is a dictionary keyed by OpenSocial ID
   viewerData = appdata[viewer.getId()];

   if (typeof viewerData == 'undefined' || viewerData == null) {
      setStatus("No data to load.");
      return;
   }

   var favcolor = viewerData["favcolor"];
   var favfood  = viewerData["favfood"];

   txt_favcolor.value = favcolor;
   txt_favfood.value = favfood;

   setStatus("Loaded successfully.");
}

function savePrefs() {
   var req = opensocial.newDataRequest();

   req.add(req.newUpdatePersonAppDataRequest(
       idspec_string, "favcolor", txt_favcolor.value));
   req.add(req.newUpdatePersonAppDataRequest(
       idspec_string, "favfood", txt_favfood.value));

   req.send(onDataSaved);
   setStatus("Request sent. Waiting for reply...");
};

function onDataSaved(data) {
   if (data.hadError()) {
      setStatus("*** error saving: " + data.getErrorMessage());
      // handle error here
      return;
   }
   setStatus("Saved successfully.");
}

function delPrefs() {
   var req = opensocial.newDataRequest();
   req.add(req.newRemovePersonAppDataRequest(
      idspec_string, [ "favcolor", "favfood"]), 'data');
   req.send(onDataDeleted);
   setStatus("Deletion request sent, waiting for reply...");
}

function onDataDeleted(data) {
   setStatus(data.hadError() ? "*** error deleting data: " 
     + data.getErrorMessage() : "Data deleted");
}

gadgets.util.registerOnLoadHandler(init);
</script>

What About Other People's Data?

You may be asking yourself: if you can read and write your own appdata, can you also do the same with his or her friends' data? Remember that the data is stored per person per application, so you cannot access data written by other applications, but you may have access to the data that your application saved associated to other viewers. The table below summarizes what is allowed in each possible scenario.

Target Can read appdata? Can write appdata?
Viewer yes yes
Viewer's friend with application installed yes no
Viewer's friend without application installed no no

How Much Data Can I Store?

A small amount. While we don't specify how much, you should only use this to store very small bits of data like configuration settings. Anything more elaborate should be stored in your own infrastructure. For your safety, please keep backups of all the data you store using this feature.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.