- The Two Concepts That Every Developer Should Know
- Starting Our Tic-Tac-Toe App
- Accessing MySpace User Data
- Error Handling
- Summary
Accessing MySpace User Data
MySpace Profile data is represented in OpenSocial by the opensocial.Person object. Essentially,
opensocial.Person = a MySpace Profile
The opensocial.Person object contains the methods shown in Table 2.1. In addition, the opensocial.Person object supports a wide range of data fields, such as a user's ID, Profile picture, or favorite movies. Table 2.2 presents all of the supported person fields along with their return types.
Table 2.1. opensocial.Person Methods*
Method |
Purpose |
getDisplayName() |
Gets a text display name for this person; guaranteed to return a useful string |
getField(key, opt_params) |
Gets data for this person that is associated with the specified key |
getId() |
Gets an ID that can be permanently associated with this person |
isOwner() |
Returns true if this person object represents the owner of the current page |
isViewer() |
Returns true if this person object represents the currently logged-in user |
Table 2.2. MySpace opensocial.Person Fields*
MySpace-Supported Person Field |
Description |
Return Type |
opensocial.Person.Field.ABOUT_ME |
A general statement about the person |
String |
opensocial.Person.Field.AGE |
Person's age |
Number |
opensocial.Person.Field.BODY_TYPE |
An object containing the person's body type; MySpace returns only opensocial.BodyType.Field.BUILD and opensocial.BodyType.Field.HEIGHT |
opensocial.BodyType |
opensocial.Person.Field.BOOKS ** |
Person's favorite books |
Array of strings |
opensocial.Person.Field.CHILDREN |
Description of the person's children |
String |
opensocial.Person.Field.CURRENT_LOCATION |
Person's current location; MySpace returns only opensocial.Address.Field.REGION, opensocial.Address.Field.COUNTRY, and opensocial.Address.Field.POSTAL_CODE |
opensocial.Address |
opensocial.Person.Field.DATE_OF_BIRTH |
Person's date of birth |
Date |
opensocial.Person.Field.DRINKER |
Person's drinking status (with the enum's key referencing opensocial.Enum.Drinker) |
opensocial.Enum |
opensocial.Person.Field.ETHNICITY |
Person's ethnicity |
String |
opensocial.Person.Field.GENDER |
Person's gender (with the enum's key referencing opensocial.Enum.Gender) |
opensocial.Enum |
opensocial.Person.Field.HAS_APP |
Whether or not the person has added the app |
Boolean |
opensocial.Person.Field.HEROES ** |
Person's favorite heroes |
Array of strings |
opensocial.Person.Field.ID |
MySpace user ID |
String |
opensocial.Person.Field.INTERESTS ** |
Person's interests, hobbies, or passions |
Array of strings |
opensocial.Person.Field.JOBS |
Jobs the person has held; MySpace returns only opensocial.Organization.Field.NAME and opensocial.Organization.Field.TITLE |
Array of opensocial.Organization |
opensocial.Person.Field.LOOKING_FOR |
Person's statement about whom or what he or she is looking for or what he or she is interested in meeting people for |
opensocial.Enum |
opensocial.Person.Field.MOVIES ** |
Person's favorite movies |
Array of strings |
opensocial.Person.Field.MUSIC ** |
Person's favorite music |
Array of strings |
opensocial.Person.Field.NAME |
An object containing the person's name; MySpace returns only opensocial.Name.Field.UNSTRUCTURED |
opensocial.Name |
opensocial.Person.Field.NETWORK_PRESENCE |
Person's current network status (with the enum's key referencing opensocial.Enum.Presence) |
opensocial.Enum |
opensocial.Person.Field.NICKNAME |
Person's nickname |
String |
opensocial.Person.Field.PROFILE_SONG |
Person's Profile song |
opensocial.Url |
opensocial.Person.Field.PROFILE_URL |
Person's Profile URL |
String (must be fully qualified) |
opensocial.Person.Field.RELATIONSHIP_STATUS |
Person's relationship status |
String |
opensocial.Person.Field.RELIGION |
Person's religion or religious views |
String |
opensocial.Person.Field.SEXUAL_ORIENTATION |
Person's sexual orientation |
String |
opensocial.Person.Field.SMOKER |
Person's smoking status (with the enum's key referencing opensocial.Enum.Smoker) |
opensocial.Enum |
opensocial.Person.Field.STATUS |
Person's status |
String |
opensocial.Person.Field.THUMBNAIL_URL |
Person's photo thumbnail URL |
String (must be fully qualified) |
opensocial.Person.Field.TV_SHOWS |
Person's favorite TV shows |
Array of strings |
opensocial.Person.Field.URLS |
URLs related to the person or the person's Web pages or feeds; returns only the Profile URL, so it's better to use opensocial.Person.Field.PROFILE_URL instead |
Array of opensocial.Url |
MyOpenSpace.Person.Field.MEDIUM_IMAGE |
Medium-sized version of the image returned by THUMBNAIL_URL; MySpace returns opensocial.Url.Field.ADDRESS and opensocial.Url.Field.TYPE. This field is not part of the OpenSocial spec and is a MySpace-specific extension. |
opensocial.Url |
MyOpenSpace.Person.Field.LARGE_IMAGE |
Large version of the image returned by THUMBNAIL_URL; MySpace returns opensocial.Url.Field.ADDRESS and opensocial.Url.Field.TYPE. This field is not part of the OpenSocial spec and is a MySpace-specific extension. |
opensocial.Url |
Accessing Profile Information Using the opensocial.Person Object
The first thing we're going to add to our basic Tic-Tac-Toe app is the ability to get the default fields for our Viewer by fetching the corresponding opensocial.Person object using an opensocial.DataRequest. MySpace defines these default opensocial. Person.Field values for the Person object:
- Profile URL, or opensocial.Person.Field.PROFILE_URL
- Image URL, or opensocial.Person.Field.THUMBNAIL_URL
- Display name, or opensocial.Person.Field.NAME
- User ID, or opensocial.Person.Field.ID
These are the bare-minimum default fields that, as long as you have access to the Person object, will always be returned.
One thing to note is that all requests to the MySpace API are asynchronous, meaning you launch them as "fire and forget" requests. Typically you make a request and specify a callback function; when the server is done processing your request, it passes the results as a parameter into your callback function. One result of this is that you're probably going to want to put your data requests at the start of the code. That way the MySpace API can start processing your request as soon as possible, and your app won't be stuck waiting for data to come back.
Let's take a look at a function that does the following:
- Instantiates an opensocial.DataRequest object
- Calls newFetchPersonRequest to create a generic request object, passing in the VIEWER as a parameter
- Adds the request to the queue by calling add(), passing in a key to name the request
The following code shows this function:
/** * Makes the initial call for the Viewer's opensocial.Person * object and requests all fields supported by MySpace */ function getInitialData(){ // Returns an opensocial.DataRequest object var req = opensocial.newDataRequest(); // Create a request object, passing in the Viewer; this will // specify we want the opensocial.Person for the Viewer var viewer_req = req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER); // Add the request to the queue and give it a key req.add(viewer_req, "v"); // Start processing the requests and specify the callback function req.send(getDataCallback); }
The first line of the function simply returns an opensocial.DataRequest object. The DataRequest object is designed to take care of all your requesting needs. While developing our app, we'll make heavy use of it.
opensocial.IdSpec.PersonId.VIEWER is one of many enums you'll encounter in OpenSocial. In JavaScript this actually resolves into a string, and on MySpace specifically it is equal to the string VIEWER. You can use the string and enum interchangeably, but we recommend using the enum for two reasons:
- If you mistype the enum, your mistake triggers a JavaScript error, but if you mistype the string, it isn't immediately obvious that something went wrong, so it is harder to debug.
- The values to which the enums resolve aren't yet clearly outlined in the OpenSocial spec, so different containers might resolve enums to different strings.
We use opensocial.IdSpec.PersonId.VIEWER here to state that we want to fetch the Viewer's data. Had we wanted to fetch the Owner's data, we would have used opensocial.IdSpec.PersonId.OWNER.
newFetchPersonRequest takes the ID of the user you want to request as a parameter (in this case the Viewer) and returns an object. That object is actually unnamed by OpenSocial, and the documentation simply refers to it as a "request." This object is never used directly beyond how it's used here—passed into DataRequest's add() function.
Essentially, the DataRequest object creates "request" objects that are passed into its own add function. It may seem a little confusing, but remember—you'll never have to use that mysterious, unnamed "request" object directly, and all OpenSocial API calls follow this general pattern.
The add function takes that request and adds it to a queue that is stored in the DataRequest object. We also give the request a key and, in this case, the string "v". This key is used to identify the response of a specific request; we'll use this key later when we talk about how to parse responses.
The last line tells the container to start processing the request queue by calling send(). We pass the name of our callback function into send() as a parameter; when the server is done processing our request, it calls this function while passing in the response. You can see the flow of the OpenSocial data request and response pattern in Figure 2.2.
Figure 2.2 Basic request and response pattern for OpenSocial.
Getting More than Just the Default Profile Data
Fields beyond the default person fields typically follow a slightly different request pattern and may also require higher permission levels for access.
As an example of this, we'll be requesting our Viewer's status message (like a headline or shout-out), address, and gender. We've modified the function shown previously to now specify that we want this additional data:
/** * Makes the initial call for the Viewer's opensocial.Person * object and requests all fields supported by MySpace */ function getInitialData(){ // Returns an opensocial.DataRequest object var req = opensocial.newDataRequest(); // Used to specify what data is returned in the response var fields = [opensocial.Person.Field.CURRENT_LOCATION, opensocial.Person.Field.GENDER, opensocial.Person.Field.STATUS]; // Create an empty object to use for passing in the parameters; // the parameters here are additional person fields from above var opt_params = {}; // Add the list of fields to the parameters opt_params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = fields; // Create a request object, passing in the Viewer; this will // specify we want the opensocial.Person for the Viewer var viewer_req = req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER,opt_params); // Add the request to the queue and give it a key req.add(viewer_req, "v"); // Start processing the request and specify the callback function req.send(getDataCallback); }
Let's take a look at what's changed. The first line is the same; we instantiate an opensocial.DataRequest object. In the next line we create an array of opensocial.Person.Field enums; this list corresponds to the specific Person fields we want from the API.
Next, an empty object is created called opt_params. This is another OpenSocial pattern that you'll see repeated in many places. Requests typically have some default action, and if you want to modify the default action, you need to supply some parameters to define what you want to do. You do this by adding certain properties to an object and passing that object into the request.
When the MySpace container is processing a request for Viewer or Owner data, it checks the parameters sent in for a property named profileDetail. If it finds such a property, it knows that additional fields were requested. So let's take a look at that line again:
// Add the list of fields to the parameters opt_params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = fields;
On MySpace, opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS resolves to the string profileDetail. We're creating that property and setting it equal to the array of opensocial.Person.Field values we created earlier. The container now knows that additional fields were requested.
The rest of the function is the same as before—we create the request, add it to the queue, and send it off.
opensocial.DataResponse and opensocial.ResponseItem (aka, Using MySpace User Data)
The various person fields are returned in different ways, but there are essentially three types of responses:
- A literal, such as a string or a number. For example, opensocial.Person.Field.STATUS returns a string.
- An object. For example, opensocial.Person.Field.CURRENT_LOCATION returns an opensocial.Address object.
- An enum. For example, opensocial.Person.Field.GENDER returns an opensocial.Enum enum object.
All responses from the API come in the form of an opensocial.DataResponse object. The DataResponse object contains some number of opensocial.ResponseItem objects; each ResponseItem corresponds to the requests we added to the DataRequest queue. For now, we just have the one request for the Viewer; we'll look at dealing with multiple requests and ResponseItem objects in the next chapter.
To get a particular ResponseItem object from an opensocial.DataResponse, you need to use a key. If you already provided an optional key, use that. Otherwise, you can use the default key created by the MySpace container.
/** * The callback method for the initial request * @param {opensocial.DataResponse} data */ function getDataCallback(data){ // "v" was the key created in the request, // data is an object of type opensocial.DataResponse // data.get("v") returns an opensocial.ResponseItem // data.get("v").getData() will return the actual data, // in this case an opensocial.Person object viewer = data.get("v").getData(); // Now let's do something with all that data printPerson(); }
Let's take a look at this function more closely. The first thing to notice is that it is the function we specified when we called send() in our previous request function.
// Start processing the request and specify the callback function req.send(getDataCallback);
The function getDataCallback is executed when the server has processed our request and prepared a response. The first line in getDataCallback takes that DataResponse object and calls the get() function, passing in our key. Above we named our Viewer request "v"; here we use that key to get the ResponseItem that corresponds to that request.
The function call data.get("v") returns the ResponseItem object that contains the Viewer's opensocial.Person object; we get the Person by calling getData(). Now the variable viewer contains an object of type opensocial.Person, which in turn contains the details of the Viewer, specifically the four default fields plus the current location, gender, and status. Table 2.3 shows the methods available for the ResponseItem object.
Table 2.3. opensocial.ResponseItem Methods*
Method |
Purpose |
getData() |
Gets the response data |
getErrorCode() |
If an error was generated by the request, returns the error code |
getErrorMessage() |
If an error was generated by the request, returns the error message |
getOriginalDataRequest() |
Returns the original data request |
hadError() |
Returns true if there was an error in fetching this data from the server |
The next section of our code calls the function printPerson(), which is a helper function that we use to parse out the details of our Viewer and output them to the screen. Here we will parse out all three types of responses—enums, strings, and objects:
/** * Output the Viewer data onto the surface */ function printPerson(){ if(null !== viewer){ // You can set the src attribute of an // <img> tag directly with THUMBNAIL_URL document.getElementById("profile_image").src = viewer.getField(opensocial.Person.Field.THUMBNAIL_URL); // getDisplayName is a shortcut for // getField(opensocial.Person.Field.NICKNAME) document.getElementById("name").innerHTML = viewer.getDisplayName(); // Get the Viewer's status var status = viewer.getField(opensocial.Person.Field.STATUS); if(status && status.length > 0){ // If the status has been set, append it after the name document.getElementById("name").innerHTML += " \"" + status + "\""; } // Get the opensocial.Address object var location = viewer.getField(opensocial.Person.Field.CURRENT_LOCATION); // The Address object is used similarly to the Person object; both pass a // field into a getField function document.getElementById("location").innerHTML = location.getField(opensocial.Address.Field.REGION); // gender is an opensocial.Enum object var gender = viewer.getField(opensocial.Person.Field.GENDER); // getDisplayValue is defined by the specific container and can and // will differ between containers and is designed for displaying only document.getElementById("gender").innerHTML = gender.getDisplayValue(); // The response of getKey is defined by OpenSocial and therefore // you can compare the result to some known value if(gender.getKey() === opensocial.Enum.Gender.FEMALE){ document.getElementById("myinfo").style.backgroundColor = "#fcf"; // pink } else{ document.getElementById("myinfo").style.backgroundColor = "#09f"; // blue } } }
Let's break down this code. To access the data from the viewer variable, which is of type opensocial.Person, we'll typically need to use the getField() function. After we confirm that viewer isn't null, we call getField(), pass in the field we want as a parameter, and set that value to the src attribute of an image tag. The field we asked for, opensocial.Person.Field.THUMBNAIL_URL, is returned as a string, so we can just access it directly. That's really all there is to parsing out a Person field that is returned as a string. For details on the return type for every supported Person field, refer back to Table 2.2 in this chapter.
The next line uses a shortcut to retrieve the Viewer's display name. Another shortcut, getID(), exists to retrieve the Viewer's ID. All other fields are accessed using getField(), as you'll see in the next few lines. We could have used getField(opensocial.Person.Field.NICKNAME) to retrieve the display name, but the shortcut is cleaner.
Next, we parse out the Viewer's current location; this field is returned as an opensocial.Address object. You'll notice that the opensocial.Address object behaves similarly to the opensocial.Person object. Both have fields that describe the types of data you can retrieve, which in turn are fetched using the getField() function. Many of the objects in OpenSocial follow this pattern.
Finally, we parse out the gender, which is an opensocial.Enum object. There are two useful types of data in each opensocial.Enum: the display value and the key. The display value is retrieved using getDisplayValue() and is a string that is defined by each container as it sees fit. For example, the display value for the female gender, depending on the container, could be "female" or "It's a girl!" or even "http://example.com/girl.jpg." Because these display values can be different from container to container, the key is typically used when making comparisons as the key values are defined in the OpenSocial spec.
In our code, we output the display value to the surface but use the key to make a logical comparison; we set the background color to pink if the gender is female (i.e., it's equal to opensocial.Enum.Gender.FEMALE), or blue otherwise.
In the full code listing for the chapter, which you can find at http://code.google.com/p/opensocialtictactoe/source/browse/#svn/trunk/chapter2, we added a function that goes through each Person field and parses it. If you're wondering how to parse a particular field that wasn't covered here, take a look at the code and you should be able to find what you need.