Chapter 2

UPDATED: 10/19/2008 I have updated this for easier viewing of the recipes, and I have also added more "Chapters" to this Mini Cookbook. See below for those links to the "Chapters".

  1. AMFPHP Recipes
  2. Web Service Recipes
  3. Air Recipes
  4. Cairngorm Recipes

Introduction

We are going to take alook at the different ways for a Flex or Air application can use to access server side services. Each recipe has a problem and a solution, followed by a discussion of what happened and how we did what we did.

Table of Contents

Web Service Recipes

 

Recipe 2.1 - Getting Started with XML-RPC

Problem: You want to connect to a XML-RPC enabled weblog to get you posts or even just to connect.

Solution: Pick up this RPC ActionScript 3 library from Google code, you have to fix one thing inside one of the classes its on the Wiki, its a quick fix that we have to do because they haven’t updated it yet, and you will be ready to go. Here a simple call to WordPress’s XML-RPC.

Since we are going to make some calls to WordPress lets take a look at one method that we are going to call, examine it and see how we can make this call from Flex.

Below is the "blogger.getUserInfo" method, the method says that it excepts 2 parameters, a username and a password. Here is the function.

   1:  /* blogger.getUsersInfo gives your client some info about you, so you don't have to */
   2:     function blogger_getUserInfo($args) {
   3:   
   4:        $this->escape($args);
   5:   
   6:        $user_login = $args[1];
   7:        $user_pass  = $args[2];
   8:   
   9:        if (!$this->login_pass_ok($user_login, $user_pass)) {
  10:           return $this->error;
  11:        }
  12:   
  13:        set_current_user( 0, $user_login );
  14:        if( !current_user_can( 'edit_posts' ) )
  15:           return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this blog.' ) );
  16:   
  17:        do_action('xmlrpc_call', 'blogger.getUserInfo');
  18:   
  19:        $user_data = get_userdatabylogin($user_login);
  20:   
  21:        $struct = array(
  22:           'nickname'  => $user_data->nickname,
  23:           'userid'    => $user_data->ID,
  24:           'url'       => $user_data->user_url,
  25:           'lastname'  => $user_data->last_name,
  26:           'firstname' => $user_data->first_name
  27:        );
  28:   
  29:        return $struct;
  30:     }

 

But if we use this code below to call this method from Flex, to the average Joe it looks ok right?

 

   1:  /**
   2:   * blogger.getUserInfo
   3:   * 
   4:   * @args username password
   5:   */
   6:   private function getUserInfo():void
   7:   {
   8:     service.call( "blogger.getUserInfo", txt_username.text, 
   9:                                 txt_password.text );
  10:   }

 

Wrong, we have to insert some jumble code I guess before our username and password for the call to actually work. I think it has something to do with blogger API using and apikey before or something.

So we use this code instead.

 

   1:  /**
   2:  * blogger.getUserInfo
   3:  * 
   4:  * @args username password
   5:  */
   6:  private function getUserInfo():void
   7:  {
   8:     service.call( "blogger.getUserInfo", "0000000", txt_username.text,    
   9:                                      txt_password.text );
  10:  }

 

For WordPress methods they are straight forward, take this for example to get the users blogs:

Discussion: Now that we have figured out why the blogger methods weren’t working we can start creating a class to be able to call all of the methods that are defined inside the XML-RPC script on our server. I’ll leave that to you though.

 

Back to top

 

Recipe 2.2 - Requests with XML-RPC

Problem: You want to authenticate the user before making calls, well you just want to send over some arguments and stuff.

Solution: Create some forms and code up some new functions to do exactly what you need.

Here are some functions that will get you prepared to send and receive data.

   1:  //This holds our data
   2:  [Bindable] private var returnedData:ArrayCollection;
   3:   
   4:  //Wordpress URL
   5:  [Bindable] private var wordpressEndpoint:String = "http://localhost/wordpress";
   6:   
   7:  //Our Service variable that is a xmlrpc object 
   8:  private var service:XMLRPCObject;
   9:   
  10:  private function init():void
  11:  {
  12:     //Create a new service xmlrpc object
  13:     service = new XMLRPCObject();
  14:     
  15:     //Specify the endpoint
  16:     service.endpoint = wordpressEndpoint;
  17:     
  18:     //We know that the destination is /xmlrpc.php
  19:     service.destination = "/xmlrpc.php";
  20:     
  21:     //Set a fault handler
  22:     service.addEventListener( FaultEvent.FAULT, onFault );
  23:     
  24:     //Set a result handler
  25:     service.addEventListener( ResultEvent.RESULT, onResult );            
  26:  }
  27:   
  28:  /**
  29:   * wp.getUsersBlogs
  30:   * 
  31:   * @args username password
  32:   */
  33:   private function getUsersBlogs():void
  34:   {
  35:     service.call( "wp.getUsersBlogs", txt_username.text, 
  36:                             txt_password.text );
  37:   }
  38:   
  39:  /**
  40:   * blogger.getRecentPosts
  41:   * 
  42:   * @args blog_id, username, password, how many
  43:   */
  44:   private function getRecentPosts():void
  45:   {
  46:     service.call( "blogger.getRecentPosts", "00000000", txt_blogid.value,  
  47:                                            txt_username.text, 
  48:                                            txt_password.text, 
  49:                                            txt_howmany.value );
  50:   }      
  51:  //Result handler
  52:  private function onResult( w_result:ResultEvent ):void
  53:  {
  54:     returnedData = new ArrayCollection( ArrayUtil.toArray( w_result.result ) );
  55:     
  56:     //Trace 
  57:     trace( w_result.result );
  58:  }
  59:        
  60:  //Fault handler
  61:  private function onFault( w_fault:FaultEvent ):void
  62:  {
  63:     Alert.show( w_fault.fault.faultString, w_fault.fault.faultCode );
  64:     
  65:     //Trace
  66:     trace( w_fault.fault.faultDetail );
  67:  }      

 

And here is the view that will make the calls and show the data.

 

   1:  <mx:Form>
   2:     
   3:        <!--Wordpress Username-->
   4:        <mx:FormItem label="Username:" 
   5:           required="true">
   6:           <mx:TextInput id="txt_username"/>
   7:        </mx:FormItem>
   8:        
   9:        <!--Wordpress Password-->
  10:        <mx:FormItem label="Password:" 
  11:           required="true">
  12:           <mx:TextInput id="txt_password" 
  13:              displayAsPassword="true"/>
  14:        </mx:FormItem>
  15:        
  16:        <!--Wordpress Submit Credentials-->
  17:        <mx:FormItem>
  18:           <mx:Button label="Check"
  19:              click="getUserInfo()"/>
  20:        </mx:FormItem>
  21:        
  22:        <mx:FormHeading label="Recent Posts"/>
  23:        
  24:        <!--Wordpress Blog ID, usually 1-->
  25:        <mx:FormItem label="Blog ID:" 
  26:           required="true">
  27:           <mx:NumericStepper id="txt_blogid"/>
  28:        </mx:FormItem>
  29:        
  30:        <!--Wordpress, How Many Posts?-->
  31:        <mx:FormItem label="How Many:"
  32:           required="true">         
  33:           <mx:NumericStepper id="txt_howmany"
  34:              maximum="25"
  35:              minimum="1"/>      
  36:        </mx:FormItem>
  37:        
  38:        <mx:FormItem>
  39:           <mx:Button label="Get Recent Posts"
  40:              click="getRecentPosts()"/>
  41:        </mx:FormItem>         
  42:     </mx:Form>
  43:     
  44:     <mx:HBox width="100%">
  45:        <mx:Button label="Get Users Blogs"
  46:           click="getUsersBlogs()"/>
  47:     </mx:HBox>
  48:     
  49:     <mx:DataGrid id="dg_data" 
  50:        width="100%" 
  51:        height="100%" 
  52:        dataProvider="{ returnedData }"/>

 

Discussion: Now that you got the hang of things, you can start making a full service class that will allow creating and editing posts, pages, and categories.

 

Recipe 2.3 - Authentication and XML-RPC

Problem: Ok I got the hang of this XML-RPC thing, but what if my server requires authentication before making any requests for data? What am I suppose to do?

Solution: Simple, we will slightly modify the calls to our service by adding authentication headers with the call, encrypting the username and password with every request on the server, supplying its requirements and receiving our data.

We are going to be using the same as3rpc library as before and for the service we are going to be using the big social networking site Buzznet. You need an apikey to make calls so grab one and you will be set.

First add a xmlrpc object to your view like this.

   1:  <xmlrpc:XMLRPCObject id="buzznetSrv" 
   2:     destination="xmlrpc/?key={ apikey }" 
   3:     endpoint="http://bnedit.buzznet.com/interface/" 
   4:     fault="onFault( event )"
   5:     result="onResult( event )"/>

 

Here is how to set up the authentication for the calls.

 

   1:  /* Authenticate the User */
   2:  private function doLogin():void
   3:  {
   4:     var encode:Base64Encoder = new Base64Encoder();
   5:        encode.encode( txt_username.text + ":" + txt_password.text );
   6:     
   7:     buzznetSrv.method = "POST";
   8:     buzznetSrv.contentType = "application/xml";
   9:     buzznetSrv.useProxy = false;
  10:     buzznetSrv.headers = { Authorization: "Basic " + encode, Accept: "application/xml" };
  11:     buzznetSrv.call( "buzznet.getOnlineNow" );
  12:  }                  

 

What we do here is first create a variable that is type Base64Encoder and then we tell it to encode the values from the username input plus a ":" and then the values of the password input. This makes one string with a username password seperated by a colon to add to our header.

Then we set up the method, the content type, and create the header, we then set the Authorization: "Basic" with our encoded credentials to the header. Finishing it off by sending a request to get online users. And we do get a response and this is what we do with that.

 

   1:  //Data holder for the requests
   2:  [Bindable] private var dataCollection:ArrayCollection;
   3:   
   4:  //Hides loading and creates a new arraycollection from the result
   5:  private function onResult( event:ResultEvent ):void
   6:  {   
   7:     //Create a new arraycollection from the result
   8:     dataCollection = new ArrayCollection( ArrayUtil.toArray( event.result ) );
   9:  }   

 

And now to show the data.

 

   1:  <mx:VBox width="225" height="100%" styleName="paddingBox">
   2:   
   3:  <!--Buzznet Username-->
   4:  <mx:Label text="Username:" 
   5:     fontWeight="bold"/>
   6:  <mx:TextInput id="txt_username" 
   7:     width="100%" 
   8:     text=""/>
   9:   
  10:  <!--Buzznet Password-->
  11:  <mx:Label text="Password:" 
  12:     fontWeight="bold"/>
  13:  <mx:TextInput id="txt_password" 
  14:     displayAsPassword="true" 
  15:     width="100%" 
  16:     text=""/>
  17:     
  18:  <!--Authenticate Button-->
  19:  <mx:Button 
  20:     label="Authenticate" 
  21:     click="doLogin()"/>
  22:              
  23:  </mx:VBox>
  24:   
  25:  <mx:DataGrid id="dg_data" 
  26:     width="100%" 
  27:     height="100%" 
  28:     dataProvider="{ dataCollection }"/>   

 

Discussion: Wow we just about can do anything now, XML-RPC, authentication, man we are on top of the world right now!

 Back to top

 

Recipe 2.4 - Getting Started with REST

Problem: I want to make some RESTful calls to an API, but I don’t know where to start.

Solution: Create a function to test our REST calls to the 30boxes API and see what happens.

All calls to 30boxes must have a APIKEY, also for user data you must send a token that the user has been given allowing your application to access the users data. We will take care of that next.

First lets call one of there sample methods "test.Ping".

We need to set up our calls and make it easy to send out requests so lets use the following.

 

   1:  private var service:HTTPService;
   2:  private var baseURL:String = "http://30boxes.com/api/api.php?";
   3:   
   4:  private function init():void
   5:  {
   6:     service =  new HTTPService();
   7:  }
   8:   
   9:  /**
  10:   * Helper for sending our queries
  11:   *  
  12:   * @param aURI the built url to send
  13:   * @param aResultHandler a result handler for the call
  14:   * 
  15:   */      
  16:  private function sendQuery( aURI:URI, aResultHandler:Function ):void
  17:  {
  18:     service.contentType = HTTPService.CONTENT_TYPE_XML;      
  19:     
  20:     service.addEventListener( ResultEvent.RESULT, aResultHandler );
  21:     service.addEventListener( FaultEvent.FAULT, onFault );
  22:     
  23:     service.url = aURI.toString();
  24:     service.send( );
  25:  }
  26:   
  27:  /**
  28:   * Test the API
  29:   *  
  30:   * @param aApiKey
  31:   * 
  32:   */      
  33:  public function testPing( aApiKey:String ):void
  34:  {
  35:     var theURL:URI = new URI( baseURL );
  36:        theURL.setQueryValue( "method", "test.Ping" );
  37:        theURL.setQueryValue( "apiKey", aApiKey );
  38:     
  39:     sendQuery( theURL, onResult_testPing );
  40:     trace( theURL.toString() );
  41:  }
  42:   
  43:  private function onResult_testPing( t_result:ResultEvent ):void
  44:  {
  45:     trace( t_result.result as XML );
  46:  }

Discussion: Ok we start out by setting up our request the we create a function called sendQuery, and what this function does is it takes the service variable and builds it up so that when other functions call on this sendQuery function the passed arguments are being used to send the query and handle the result.

When we create the testPing function we are requiring one argument which is a apikey and then creating a variable type URI so we can set up name/value pairs for the call. We declare the method and the name and the "test.Ping" as the value, then since this method only requires an apikey parameter we set up that name/value pair with "apiKey" and the value aApiKey.

Once we have that set up we call the sendQuery function and filling the arguments with our URI and a result function.

Back to top

 

Recipe 2.5 - REST Service Class

Problem: We are set up for making calls, and familiar with REST. Now can we see how to make more calls to 30boxes.

Solution: We will create a small class that will be the container for all of our calls we want to make to 30boxes.

Create a new class called T30boxesResultEvent.as enter the following

   1:  package com.jonniespratley.webapis.T30boxes.events
   2:  {
   3:     import mx.messaging.messages.IMessage;
   4:     import mx.rpc.AsyncToken;
   5:     import mx.rpc.events.ResultEvent;
   6:   
   7:     
   8:     public class T30boxesResultEvent extends ResultEvent
   9:     {      
  10:        static public const OK:String = "ok";
  11:        static public const TOKEN_LOADED:String = "TOKEN_LOADED";
  12:        static public const AUTHORIZED:String = "AUTHORIZED";      
  13:        static public const USER_LOADED:String = "USER_LOADED";      
  14:        static public const EVENTS_RESULT:String = "EVENTS_RESULT";      
  15:        static public const TODOS_RESULT:String = "TODOS_RESULT";
  16:        
  17:        public function T30boxesResultEvent( type:String, 
  18:                                    bubbles:Boolean=false, 
  19:                                    cancelable:Boolean=true, 
  20:                                    result:Object=null, 
  21:                                    token:AsyncToken=null, 
  22:                                    message:IMessage=null )
  23:        {
  24:           super( type, bubbles, cancelable, result, token, message );
  25:        }
  26:     }
  27:  }

This result even class is what is going to get dispatched when we receive a result from the server and we will pass our data along with the event.

Now we need to create our fault class, here is the code for that.

T30boxesFaultEvent.as:

   1:  package com.jonniespratley.webapis.T30boxes.events
   2:  {
   3:     import mx.messaging.messages.IMessage;
   4:     import mx.rpc.AsyncToken;
   5:     import mx.rpc.Fault;
   6:     import mx.rpc.events.FaultEvent;
   7:     
   8:     public class T30boxesFaultEvent extends FaultEvent
   9:     {
  10:        static public const FAIL:String = "fail";
  11:        static public const USER_FAULT:String = "USER_FAULT";
  12:        static public const EVENTS_FAULT:String = "EVENTS_FAULT";
  13:        static public const TODOS_FAULT:String = "TODOS_FAULT";
  14:        
  15:        public function T30boxesFaultEvent( type:String, 
  16:                                   bubbles:Boolean=false, 
  17:                                   cancelable:Boolean=true, 
  18:                                   fault:Fault=null, 
  19:                                   token:AsyncToken=null, 
  20:                                   message:IMessage=null )
  21:        {
  22:           super( type, bubbles, cancelable, fault, token, message );
  23:        }
  24:     }
  25:  }

 

I like using interfaces so lets create a new interface called IT30boxesService.as

 

   1:  package com.jonniespratley.webapis.T30boxes
   2:  {
   3:     public interface I30boxesService
   4:     {
   5:        function testPing( aApiKey:String ):void
   6:        
   7:        function userAuthorize( aApiKey:String,
   8:                          aApplicationName:String, 
   9:                          aApplicationLogo:String, 
  10:                          aReturnUrl:String = null ):String
  11:                          
  12:        function userGetAllInfo( aApiKey:String, 
  13:                           aAuthorizedToken:String ):void      
  14:     }
  15:  }



Our interface is just going to hold the functions that we want to call, this will be a very small interface for this example, but when we choose to extend the service class we must change the interface before the class because when a class implements a interface it basically signs a contract to include all of the functions defined inside of the interface. But the interface holds no functionality, its like guidelines for classes that choose to implement it.

Functions Explained

  1. testPing; This function requires an API Key before sending
  2. userAuthorize; This function returns a string, in that string will be all of the necessary elements to redirect your use to 30boxes.com were they will get a token. This token basically says ok I know that this application is accessing my data.
  3. userGetAllInfo; This function requires an API Key and the token from the user, returning all information about that user who’s token was sent.

Then we need to an service class that will implement our interface. This class is where we bring functionality to the functions we defined inside our interface.

Here is a portion of our T30boxesService.as:

 

   1:  package com.jonniespratley.webapis.T30boxes
   2:  {
   3:     import com.adobe.net.URI;
   4:     import com.jonniespratley.webapis.T30boxes.events.*;   
   5:     import flash.events.EventDispatcher;
   6:     
   7:     import mx.rpc.AsyncToken;
   8:     import mx.rpc.events.FaultEvent;
   9:     import mx.rpc.events.ResultEvent;
  10:     import mx.rpc.http.HTTPService;
  11:     
  12:     /**
  13:      * 30boxes.com API Service Class. This class is a starting point for
  14:      * the 30boxes.com AS3 Library. It provides most of the calls that
  15:      * 30boxes supports with there REST API.
  16:      * 
  17:      * @langversion ActionScript 3.0
  18:      * @playerversion Flash 9
  19:      * @author Jonnie 
  20:      * 
  21:      */   
  22:     public class T30boxesService extends EventDispatcher implements I30boxesService
  23:     {
  24:        private var service:HTTPService;
  25:        private var baseURL:String = "http://30boxes.com/api/api.php?";
  26:        
  27:        //Authorize Application Info
  28:        public var APP_NAME:String = "";
  29:        public var APP_LOGO:String = "";
  30:        public var APP_RETURN_URL:String = "";
  31:        
  32:        public function T30boxesService()
  33:        {
  34:           service =  new HTTPService();         
  35:        }
  36:        
  37:        /**
  38:         * Helper for sending our queries
  39:         *  
  40:         * @param aURI the built url to send
  41:         * @param aResultHandler a result handler for the call
  42:         * 
  43:         */      
  44:        private function sendQuery( aURI:URI, aResultHandler:Function ):void
  45:        {
  46:           service.contentType = HTTPService.CONTENT_TYPE_XML;
  47:           service.resultFormat = HTTPService.RESULT_FORMAT_E4X;         
  48:           
  49:           service.addEventListener( ResultEvent.RESULT, aResultHandler );
  50:           service.addEventListener( FaultEvent.FAULT, onFault );
  51:           
  52:           service.url = aURI.toString();
  53:           service.send( );
  54:           var token:AsyncToken = service.send();
  55:        }
  56:        
  57:        /**
  58:         * Test the API
  59:         *  
  60:         * @param aApiKey
  61:         * 
  62:         */      
  63:        public function testPing(aApiKey:String):void
  64:        {
  65:           var theURL:URI = new URI( baseURL );
  66:              theURL.setQueryValue( "method", "test.Ping" );
  67:              theURL.setQueryValue( "apiKey", aApiKey );
  68:           
  69:           sendQuery( theURL, onResult_testPing );
  70:           trace( theURL.toString() );
  71:        }
  72:        
  73:        /**
  74:         * Unlike all other methods that return XML, this URL presents the user with a 
  75:         * message asking them if they want to give you permission for your application to 
  76:         * access their data. They may be required to log in first. If they choose to 
  77:         * give permission, we will provide (through the returnUrl, or a form in your application) 
  78:         * an authorizedUserToken that you need when accessing their data. This token will not 
  79:         * expire for a given user, so you may wish to store it.
  80:         * 
  81:         * Note that you cannot authorize a specific user; you are getting authorization to 
  82:         * access whomever logs in. Upon success, we tell you their identification.
  83:         * 
  84:         * @param aApiKey
  85:         * @param aApplicationName - the name of your application, up to 150 characters
  86:         * @param aApplicationLogo - the url-encoded location of your logo
  87:         * @param aReturnUrl - the url-encoded location where we will return to with a user token. 
  88:         * All web-based applications should provide a URL. If it is not provided (which would be the 
  89:         * case for client applications), we will instruct the user to copy the token to your application
  90:         * 
  91:         * @return - built url for redirection
  92:         * 
  93:         */      
  94:        public function userAuthorize( aApiKey:String, 
  95:                                aApplicationName:String, 
  96:                                aApplicationLogo:String, 
  97:                                aReturnUrl:String=null):String
  98:        {
  99:           var theURL:URI = new URI( baseURL );
 100:              theURL.setQueryValue( "method", "user.Authorize" );
 101:              theURL.setQueryValue( "apiKey", aApiKey );
 102:              theURL.setQueryValue( "applicationName", aApplicationName );
 103:              theURL.setQueryValue( "applicationLogoUrl", aApplicationLogo );
 104:           if ( aReturnUrl != null )
 105:           {
 106:              theURL.setQueryValue( "returnUrl", aReturnUrl );            
 107:           }
 108:           
 109:           return theURL.toString();
 110:           trace( theURL.toString() );
 111:        }
 112:        
 113:        /**
 114:         * This method returns private contact info and a list of the user's buddies.
 115:         * 
 116:         * @param aApiKey
 117:         * @param aAuthorizedToken
 118:         * 
 119:         */      
 120:        public function userGetAllInfo(aApiKey:String, aAuthorizedToken:String):void
 121:        {
 122:           var theURL:URI = new URI( baseURL );
 123:              theURL.setQueryValue( "method", "user.GetAllInfo" );
 124:              theURL.setQueryValue( "apiKey", aApiKey );
 125:              theURL.setQueryValue( "authorizedUserToken", aAuthorizedToken );
 126:           
 127:           sendQuery( theURL, onResult_data );
 128:        }
 129:        
 130:        private function onResult_data( t_result:ResultEvent ):void
 131:        {         
 132:           dispatchEvent( new T30boxesResultEvent( 
 133:              T30boxesResultEvent.OK, false, false, t_result.result, null, null ) );
 134:        }
 135:        
 136:        /**
 137:         * Dispatchs a T30boxes fault event
 138:         * @param t_fault
 139:         * 
 140:         */      
 141:        private function onFault( t_fault:FaultEvent ):void
 142:        {
 143:           trace( t_fault.fault );
 144:           dispatchEvent( new T30boxesFaultEvent( 
 145:              T30boxesFaultEvent.FAIL, false, false, t_fault.fault, t_fault.token, t_fault.message ) );
 146:        }      
 147:     }
 148:  }

 

This class is the holder of some methods that are on 30boxes.com, so we are bundling them up into a class for easy calling. When a function is called, we wait for a result or fault from the call, once we get that result, we dispatch one of our event classes depending on what the result was. If it was successful we dispatch a "T30boxesResultEvent.ok" we are letting whoever is listing for this event know that it happened and here is the data.

So when it comes to putting this all together to make it work, we can create a simple API Tester component that we can try out our new services. Here is the code for APITester.mxml:

   1:  <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" 
   2:     creationComplete="init()" 
   3:     width="100%" 
   4:     height="100%">
   5:     <mx:Script>
   6:        <![CDATA[
   7:           import mx.utils.ArrayUtil;
   8:           import com.jonniespratley.webapis.T30boxes.events.T30boxesResultEvent;
   9:           import mx.collections.ArrayCollection;
  10:           import mx.controls.Alert;
  11:           import com.jonniespratley.webapis.T30boxes.T30boxesService;
  12:     
  13:           private var service:T30boxesService;         
  14:           [Bindable] private var resultData:ArrayCollection;
  15:           
  16:           //Creates a new service
  17:           private function init():void
  18:           {
  19:              service = new T30boxesService();
  20:           }
  21:           
  22:           //Authorize the user
  23:           private function authorizeUser():void
  24:           {
  25:              navigateToURL( new URLRequest( service.userAuthorize( 
  26:                                      txt_apikey.text, 
  27:                                      "30Boxes of Air", 
  28:                                      "http://jonniespratley.com/images/air_boxes_logo.png", 
  29:                                   null ) ) );
  30:              
  31:              Alert.show( "Please click 'OK' once you have authorized this application on 30boxes.com.", 
  32:                 "30Boxes Authorization" );
  33:           }
  34:           
  35:           //Gets the Users info 
  36:           private function getInfo():void
  37:           {
  38:              service.userGetAllInfo(  txt_apikey.text, txt_token.text );
  39:              service.addEventListener( T30boxesResultEvent.USER_LOADED, onResult );
  40:           }
  41:     
  42:           //Handles the result
  43:           private function onResult( event:T30boxesResultEvent ):void
  44:           {
  45:              trace( "Got Event" );
  46:              resultData = new ArrayCollection( ArrayUtil.toArray( event.result ) );
  47:           }
  48:   
  49:           
  50:        ]]>
  51:     </mx:Script>
  52:     
  53:     <mx:ApplicationControlBar width="100%">
  54:        <mx:VBox width="100%">
  55:           <mx:HBox width="100%">
  56:              
  57:              <!--User Token-->
  58:              <mx:Label text="User Token:" 
  59:                 fontWeight="bold" width="75"/>
  60:              <mx:TextInput id="txt_token" 
  61:                 width="100%" 
  62:                 text=""/>
  63:                             
  64:           </mx:HBox>
  65:           <mx:HBox width="100%">
  66:              
  67:              <!--Developers API Key-->
  68:              <mx:Label text="API Key:"
  69:                 fontWeight="bold" width="75"/>
  70:              <mx:TextInput id="txt_apikey"
  71:                 width="100%" 
  72:                 text="7613785-2b78f23f6143a741e1ee7445447766b5"/>
  73:                 
  74:           </mx:HBox>
  75:        </mx:VBox>
  76:        
  77:        <!--Get Info Button-->
  78:        <mx:Button label="Get My Info"
  79:           click="getInfo()"/>
  80:        <!--Launches Web browser to get token-->
  81:        <mx:Button label="Authorize"
  82:           click="authorizeUser()"/>
  83:     
  84:     </mx:ApplicationControlBar>
  85:     
  86:     <mx:DataGrid 
  87:        width="100%" 
  88:        height="100%" 
  89:        dataProvider="{ resultData }">
  90:        <mx:columns>
  91:           
  92:           <mx:DataGridColumn 
  93:              headerText="Avatar" 
  94:              dataField="avatar">
  95:              <mx:itemRenderer>
  96:                 <mx:Component>
  97:                    <mx:Image source="{ data.avatar }"/>
  98:                 </mx:Component>
  99:              </mx:itemRenderer>
 100:           </mx:DataGridColumn>
 101:           
 102:           <mx:DataGridColumn 
 103:              headerText="First Name" 
 104:              dataField="firstName"/>
 105:           
 106:           <mx:DataGridColumn 
 107:              headerText="Last Name" 
 108:              dataField="lastName"/>
 109:           
 110:           <mx:DataGridColumn 
 111:              headerText="Time Zone"
 112:              dataField="timeZone"/>
 113:              
 114:        </mx:columns>
 115:     </mx:DataGrid>   
 116:     
 117:  </mx:VBox>

 

I have included my API Key for you to test it out, please don’t abuse it :(. But this should have you up and running with some good calls to 30boxes.com API.

You can find the full class here along with the source files for this application. If anyone would like to help me finish this class I would really appreciate it.

Discussion: That wasn’t to bad was it, now we are going to have to create a function to parse that XML data coming back. That is where you help me and show me how you do that, because I simply can’t figure it out.

 

Back to top