Subscribe: Posts / Comments / Email


RSS

Adobe Flex – Introduction to REST

Fri, Oct 3, 2008

Flex, Tutorials

Have you ever wanted to create a really sophisticated application, well connecting to a external data source is just a must with all of the APIs that are available. The social bookmarking scene is one of the biggest movements of the web.

How would you like it to be able to connect to Del.icio.us API and grab all of your bookmarks or posts, well not just yours any one who has an account at Del.icio.us, that would be pretty cool wouldn’t it?

What were are going to do here is get some of the basic information mastered when it comes to connecting to a REST API such as Del.icio.us, and from that you imagination can be the limit to where you go from here.

We need to get this thing started because I am really anxious to see what kind of applications you developers come up with.

Lets Go!

Requirements

Once you have all of those requirements lets go ahead and open up Flex Builder, and create a new project.

Show Me The Bookmarks!

Ok I see that you are excited to get this thing up and running, but we are going to have to get some nessaccary items out of the way first for the sake of having some proper objects to hold our data. This enables us to know what is coming and it also gives us the ability extend our applicatoin without having to go back and fix some files.

Here is some things we need to do:

  1. Create a data model to hold all of our data coming from Del.icio.us.
  2. Create a DeliciousPost object to hold our post data.
  3. Create a DeliciousTag object to hold our tag data.
  4. Create a DeliciousUser object to hold our user data and information.
  5. We could create a service class to make the calls for us, but…. O what the hell, lets create a DeliciousService class to hold all of our calls and result handlers.
  6. Then we need to create our views.
  7. Wire up the views.
  8. Test some calls.
  9. And Complete

So when it comes to creating our Delicious objects we are going to have to know some of the values coming from Del.icio.us when we make our calls, so lets go over them.

Here is a sample of the called method, and the result of that method.

Method: posts/recent?

Result:

Method: tags/get?

Result:

Method: posts/update?

Result:

Now that we know what is coming back we need to create our data objects. Create 3 new files DeliciousUser.as DeliciousTag.as and DeliciousPost.as make sure that they are classes.

Here is how I created mine.

I am not claiming that this way that I am showing you is the best nor most efficient way of doing things/this, so please if you know of better ways let me know. I love to get better and learn things that could make life easier.

DeliciousTag.as:

package com.jonniespratley.advguidetoflex.model
{
	[Bindable]
	public class DeliciousTag
	{
		public var tag_name:String;
		public var tag_count:String;	

		public function DeliciousTag( tag:String = "",
									  count:String = "" )
		{
			this.tag_name = tag;
			this.tag_count = count;
		}
	}
}

DeliciousPost.as

package com.jonniespratley.advguidetoflex.model
{
	import mx.collections.ArrayCollection;
	import mx.collections.IList;	

	[Bindable]
	public class DeliciousPost
	{
		public var url:String;
		public var title:String;
		public var description:String;
		public var tags:IList;
		public var shared:Boolean;
		public var timestamp:String;
		public var replace:String;			

		public function  DeliciousPost( url:String = "",
										title:String = "",
										description:String = "",
										tags:IList = null,
										shared:Boolean = true,
										timestamp:String = null )
		{
			this.url = url;
			this.title = title;
			this.description = description;
			this.tags = ( tags != null ? tags : new ArrayCollection( new Array() ) );
			this.timestamp = timestamp;
			this.shared = shared;
		}
	}
}

DeliciousUser.as

package com.jonniespratley.advguidetoflex.model
{

	[Bindable]
	public class DeliciousUser
	{
		public var user_name:String;
		public var user_pass:String;
		public var user_inbox:Number;
		public var user_update:String;
		public var user_feed:String;		

		public function  DeliciousUser( username:String,
										password:String,
										updated:String = "",
										messages:Number = 0 )
		{
			this.user_name = username;
			this.user_pass = password;
			this.user_update = updated;
			this.user_inbox = messages;
			this.user_feed = "http://del.icio.us/rss/" + username;
		}
	}
}

Now that we have that covered, we need to create our model so we can take our results from the server and add that data to our model so the views can display the data. Create another file called ModelLocator.as this is a ActionScript class.

ModelLocator.as

package com.jonniespratley.advguidetoflex.model
{
	import com.adobe.cairngorm.model.IModelLocator;

	import mx.collections.ArrayCollection;

    [Bindable]
	public final class ModelLocator implements IModelLocator
	{
		/** Defines the Singleton instance of the Application ModelLocator */
		private static var instance:ModelLocator;

		public function ModelLocator()
		{
			if( instance != null ) throw new Error( "Error: Singletons can only be instantiated via getInstance() method!" );

			ModelLocator.instance = this;
		}

		/** Returns the Singleton instance of the Application ModelLocator */
		public static function getInstance():ModelLocator
		{
			if( instance == null )	instance = new ModelLocator();
			return instance;
		}

		// *********** Public Variables that our views are going to bind to ************** \\
		/** Represents a del.icio.us user */
		public var deliciousUser:DeliciousUser;

		/** The data that is comming from Del.icio.us */
		public var dataCollection:ArrayCollection;

		/** The tags from Del.icio.us  */
		public var tagCollection:ArrayCollection;

		/** The tags from Del.icio.us  */
		public var postCollection:ArrayCollection;

		/** The result from the call  */
		public var callResult:String;

		/** True or False to see if we should show the result form or not */
		public var isLoggedIn:Boolean = false;

		/** The twitter data colleciton  */
		public var twitterCollection:ArrayCollection;

	}
}

Now that we have all of our data objects out of the way its time to get our DeliciousService class coded up. We will discuss the class after.

DeliciousService.as:

package com.jonniespratley.advguidetoflex.webapis
{
	import com.adobe.net.URI;
	import com.adobe.utils.DateUtil;
	import com.arc90.rpc.events.FaultEvent;
	import com.arc90.rpc.events.ResultEvent;
	import com.arc90.rpc.rest.RESTService;
	import com.arc90.rpc.rest.RESTServiceMethod;
	import com.jonniespratley.advguidetoflex.model.DeliciousPost;
	import com.jonniespratley.advguidetoflex.model.DeliciousTag;
	import com.jonniespratley.advguidetoflex.model.DeliciousUser;
	import com.jonniespratley.advguidetoflex.model.ModelLocator;

	import mx.collections.ArrayCollection;
	import mx.controls.Alert;
	import mx.managers.CursorManager;

	public class DeliciousService
	{
		/*Base API*/
		private var endpoint:String = "https://api.del.icio.us/v1/";

		/*Our REST service so we can make calls*/
		private var service:RESTService;		

		/*We want to bind the our model, so make an instance*/
		[Bindable] private var model:ModelLocator = ModelLocator.getInstance();

		public function DeliciousService()
		{
			init();
		}	

		/*When init we create a new RESTService and add a fault handler*/
		private function init():void
		{
			service = new RESTService();
			service.addEventListener(FaultEvent.FAULT, onFault );
		}

		/**
		 * This makes sending calls alot easier
		 *
		 * @param method What serivces to you want to access
		 * @param params URL parameters that are required, if any
		 * @param methodType The type of call, usually all GET
		 *
		 */
		private function sendQuery( user:DeliciousUser, uri:URI, resultHandler:Function ):void
		{
			CursorManager.setBusyCursor();
			// BASE =   https://api.del.icio.us/v1/
			// METHOD = posts/dates?
			// PARAMS = &tag=TAG
			service.setCredentials( user.user_name, user.user_pass );

			var sendQuery:String;
				sendQuery = ( endpoint );				

			//Set busy cursor
			service.showBusyCursor = true;

			//Set the url
			//service.rootURL = endpoint;

			//Set the call url
			service.url = uri.toString();

			//Set the method
//			service.request = method;

			//Send the call
			service.send();

			//Add the function param to the result handler of each call
			service.addEventListener( ResultEvent.RESULT, resultHandler );

			//Trace the bs
			trace( "\nURL Query: " + service.url );
		}		

		/**
		 * This is what we are using to check for a valid user
		 *
		 * @param user Username and Password are sent to verify user
		 */
		public function checkAccount( username:String, password:String ):void
		{
			//Set the credentials
			service.setCredentials( username, password );	

			//Set the URL
			service.url = endpoint + "posts/update";

			//Set the method
			service.method = RESTServiceMethod.GET;

			//Send it
			service.send();

			//Add an event listener
			service.addEventListener( ResultEvent.RESULT, onAccountResult );

			//Set the user to the model
			model.deliciousUser = new DeliciousUser( username, password );
		}

		/**
		 * Here is the xml result:
		 *
		 * 
		 * 	
		 *
		 * @param d_result
		 */
		private function onAccountResult( d_result:ResultEvent ):void
		{
			//Remove busy cursor manually
			CursorManager.removeBusyCursor();	

			//Check if we got a ok response
			if ( d_result.statusCode == 200 )
			{
				/* var user:DeliciousUser = new DeliciousUser(
												username,
												password,  */
	//											d_result.result.@time,
//												d_result.result.@inboxnew );									

				//Add the user data to the model
				model.deliciousUser.user_update = d_result.result.@time;
				model.deliciousUser.user_inbox = d_result.result.@inboxnew;

				//Now we can show the userform for making calls to get bookmarks and tags
				model.isLoggedIn = true;

			} else {
				Alert.show( "There was a problem connecting to Del.icio.us, try again.", "Service Error" );
			}

		}			

/* ***************************************** Tags ************************************************ */

		/**
		 * Gets all Tags
		 * @param user Delicious User
		 *
		 */
		public function getTags( user:DeliciousUser ):void
		{
			//No Params
			var params:String = "";
			var url:URI = new URI( endpoint + "/tags/get" )

			//Send to sendQuery function to get tags
			sendQuery( user, url, onResult_tags );
		}

		/**
		 * Renames a tag
		 *
		 * @param user Delicious User
		 * @param oldName Old tag name
		 * @param newName New tag name
		 *
		 */
		public function renameTag( user:DeliciousUser, oldName:String, newName:String ):void
		{
			var url:URI = new URI( endpoint + "tags/rename" );
				url.setQueryValue( "old", oldName );
				url.setQueryValue( "new", newName );

			sendQuery(user, url, onResult_tag );
		}

		/**
		 * Deletes a tag forever
		 *
		 * @param user Delicious User
		 * @param whatTag The tag to delete
		 *
		 */
		public function deleteTag( user:DeliciousUser, whatTag:String ):void
		{
			var url:URI = new URI( endpoint + "tags/delete" );
				url.setQueryValue( "tag", whatTag );

			sendQuery( user, url, onResult_tag );
		}		

		/**
		 * Handles the result, which is just a done wrapped in xml
		 *
		 * @param event
		 *
		 */
		private function onResult_tag( event:ResultEvent ):void
		{
			CursorManager.removeBusyCursor();

			trace( event.result );

			model.callResult = event.result.toString();
		}

/* ***************************************** Bundles ************************************************ */

		/**
		 * Gets all Tag bundles for that user
		 *
		 * @param user Delicious User
		 *
		 */
		public function getBundles( user:DeliciousUser ):void
		{
			var url:URI = new URI( endpoint + "tags/bundles/all" );

			sendQuery( user, url, onResult_getBundles );
		}

		/**
		 * Creates a bundle from tags
		 *
		 * @param user Delicious User
		 * @param bundle name of bundle
		 * @param tags all of the tags
		 *
		 */
		public function setBundle( user:DeliciousUser, bundle:String, tags:String ):void
		{	

			var url:URI = new URI( endpoint + "tags/bundles/set" );
				url.setQueryValue( "bundle", bundle );

			sendQuery( user, url, onResult_bundle );
		}

		/**
		 * Deletes a bundle
		 *
		 * @param user Delicious User
		 * @param whatBundle The name to be deleted
		 *
		 */
		public function deleteBundle( user:DeliciousUser, whatBundle:String ):void
		{
			var url:URI = new URI( endpoint + "tags/bundles/" );
				url.setQueryValue( "delete", whatBundle );

			sendQuery( user, url, onResult_bundle );
		}

		/**
		 * Handles the result.
		 *
		 * @param event
		 *
		 */
		private function onResult_bundle( event:ResultEvent ):void
		{
			CursorManager.removeBusyCursor();
			trace( event.result );

			model.callResult = event.result.toString();
		}

		/**
		 * Handles the reuslt for now
		 *
		 * @param event
		 *
		 */
		private function onResult_getBundles( event:ResultEvent ):void
		{
			CursorManager.removeBusyCursor();
			trace( event.result );
		}

		/**
		 * Here is the xml result:
		 *
		 * 
		 * 
		 * 		
		 * 		
		 * 		
		 * 		
		 * 
		 *
		 * Takes the result as xml, parses it and then
		 * adds the data accordingly to the tag object
		 * then adds the data to the model.
		 *
		 * @param d_result
		 *
		 */
		private function onResult_tags( d_result:ResultEvent ):void
		{

			//Remove the busy cursor manually
			CursorManager.removeBusyCursor();

			//Create a new xml object
			var resultNode:XML;			

			//Create a array to add the xml to
			var result:Array = new Array();					

				//Check if the result status code, if we get a 200 then parse the xml, else show error
				if ( d_result.statusCode == 200 )
				{
					//Make the result of the request as xml
					var tagXML:XML = new XML( d_result.result );

					//Then parse the xml, for each node in xml
					for each ( resultNode in tagXML.children() )
					{
						//Create a new Tag from the result
							//the tag_count is set to the result.@count attribute
								//the tag_name is set to the result.@tag attribute
						var tag:DeliciousTag = new DeliciousTag( resultNode.@tag, resultNode.@count );						

						//Add the tag to our array
						result.push( tag );

						//Add the array to our model for binding
						model.tagCollection = new ArrayCollection( result );
					}
				}

			//Trace the bs
			trace( "Result Headers: " + d_result.headers );
			trace( "Result Status Message: " + d_result.statusMessage );
			trace( "Result Status Code: " + d_result.statusCode );
			trace( "Result XML: " + d_result.result );
		}

/* ***************************************** Posts ************************************************ */

		/**
		 * Retrieves the most recent posts
		 *
		 * @param user delicious user
		 */
		public function getRecentPosts( user:DeliciousUser, tag:String = null, count:String = null ):void
		{
			var queryURL:URI = new URI( endpoint + "posts/recent" );

			if ( tag  )
			{
				queryURL.setQueryValue( "tag", tag );
			}
			if ( count )
			{
				queryURL.setQueryValue( "count", count );
			}

			//Send the call and set the result handler
			sendQuery( user, queryURL, onResult_posts );
		}

		public function addPost( user:DeliciousUser, post:DeliciousPost ):void
		{
			var queryURL:URI = new URI( endpoint + "posts/add" );
				queryURL.setQueryValue( "url", post.url.toString() );
				queryURL.setQueryValue( "description", post.description );
				queryURL.setQueryValue( "tags", post.tags.toArray().join( " " ) );
				queryURL.setQueryValue( "shared", post.shared ? "yes" : "no" );
				//queryURL.setQueryValue( "replace", post.replace ? "yes" : "no" );

			sendQuery( user, queryURL, onResult_post );	

		}

		private function onResult_post( event:ResultEvent ):void
		{
			CursorManager.removeBusyCursor();
			trace( event.result );
		}		

		/**
		 * Here is the xml result:
		 *
		 *

		 *

	 	 * 
		 *
		 *
		 * Takes the result as xml, parses it and then
		 * adds the data accordingly to the DeliciousPost,
		 * then adds the data to the model.
		 *
		 *
		 * @param d_result
		 *
		 */
		private function onResult_posts( d_result:ResultEvent ):void
		{
			//Remove the busy cursor manually
			CursorManager.removeBusyCursor();

			//Create a new xml object
			var resultNode:XML;			

			//Create a array to add the xml to
			var result:Array = new Array();

				//Check if the result status code, if we get a 200 then parse the xml, else show error
				if ( d_result.statusCode == 200 )
				{
					//Make the result of the request as xml
					var postXML:XML = new XML( d_result.result );

					//Then parse the xml, for each node in xml
					for each ( resultNode in postXML.children() )
					{
						var post:DeliciousPost = new DeliciousPost( resultNode.@href,
																	resultNode.@description,
																	resultNode.@extended,
																	new ArrayCollection( String( resultNode.@tag ).split( " " ) ),
																	resultNode.@shared,
																	resultNode.@time );

							//Add the post to our array
							result.push( post );						

						//And now add the result array to our model for binding
						model.postCollection = new ArrayCollection( result );
					}
				}	

			//Trace the bs
			trace( "\n\nResult Headers: " + d_result.headers.toString() );
			trace( "\nResult Status Message: " + d_result.statusMessage );
			trace( "\nResult Status Code: " + d_result.statusCode );
			trace( "\nResult XML: " + d_result.result as XML );
		}		

		/**
		 * Alerts the user of a fault, and displays the fault string.
		 *
		 * @param d_fault
		 */
		private function onFault( d_fault:FaultEvent ):void
		{
			//remove our busy cursor
			CursorManager.removeBusyCursor();

			//If Statement
			if ( d_fault.fault.faultCode == "401" )
			{
				//show alert of what happened
				Alert.show( d_fault.fault.faultString, d_fault.fault.faultCode );
				model.isLoggedIn = false;
				model.deliciousUser = null;
			}
			//Trace everything so we can fix the problems
			trace( "Fault Detail: "
					+ d_fault.fault.faultDetail + "\nFault Code: "
					+ d_fault.fault.faultCode + "\nFault String: "
					+ d_fault.fault.faultString );
			}
	}
}

When connecting to Del.icio.us all calls are accompanied by the users credentials. So for these calls to the server we have a parameter set as user:DeliciousUser, which is the user that we are getting data for. I commented the code very well so you should be able to get an idea of what is going on here.

Now you should be able to run this application ( In AIR ) and make some successful calls to Del.icio.us to retrieve you bookmarks and tags. With this basic information you just learned you can go ahead and try to extend our DeliciousSerivce class file and create some functions that will take care of the creating and deleting, as well as getting tag bundles.

So get coding and create something good!

Tags: , , ,

Leave a Reply