Snurl.com (SnipUrl) API ColdFusion Component Wrapper

Posted February 5, 2009 at 12:32 PM

Tags: ColdFusion

I was thinking of integrating my Snurl.com account (one of the many url shortening services) into KinkyTwits, my ColdFusion and jQuery powered Twitter client. Before I could do that, though, I had to create a ColdFusion component that would wrap around the SnipURL API provided by Snurl.com. The API itself has Create, Update, and Read style methods, but I only included the Create and Read wrappers as I don't see a real need to update short URLs.

Although I did run into some oddities with the UrlDecode() method this morning, the SnipURL.cfc ColdFusion component is rather simple:

 Launch code in new window » Download code as text file »

  • <cfcomponent
  • output="false"
  • hint="I provide SnipURL API functionality.">
  •  
  •  
  • <cffunction
  • name="Init"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="I return an initialized component.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Username"
  • type="string"
  • required="true"
  • hint="I am the username for the SnipURL account."
  • />
  •  
  • <cfargument
  • name="APIKey"
  • type="string"
  • required="true"
  • hint="I am the API key gotten from the SnipURL account settings."
  • />
  •  
  • <!--- Set up instance variables and store arguments. --->
  • <cfset VARIABLES.Instance = {
  • Username = ARGUMENTS.Username,
  • APIKey = ARGUMENTS.APIKey
  • } />
  •  
  • <!--- Return This reference. --->
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="CreateSnip"
  • access="public"
  • returntype="struct"
  • output="false"
  • hint="I create a snip with the given data.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="URL"
  • type="string"
  • required="true"
  • hint="I am the URL that is being shortened."
  • />
  •  
  • <cfargument
  • name="NickName"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the nickname that is associated with the short URL (will attempt to be used in the short URL)."
  • />
  •  
  • <cfargument
  • name="Title"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the title of the snip used in the snip details."
  • />
  •  
  • <cfargument
  • name="PrivateKey"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the private key."
  • />
  •  
  • <cfargument
  • name="Owner"
  • type="string"
  • required="false"
  • default=""
  • hint="I am the optional owner (if not by the given account)."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!---
  • Create the parameters collection and set required
  • values for API call.
  • --->
  • <cfset LOCAL.Parameters = {
  • SnipUser = VARIABLES.Instance.Username,
  • SnipAPI = VARIABLES.Instance.APIKey,
  • SnipLink = ARGUMENTS.URL
  • } />
  •  
  • <!--- Check for optional parametesr. --->
  • <cfif Len( ARGUMENTS.NickName )>
  •  
  • <cfset LOCAL.Parameters.SnipNick = ARGUMENTS.NickName />
  •  
  • </cfif>
  •  
  • <cfif Len( ARGUMENTS.Title )>
  •  
  • <cfset LOCAL.Parameters.SnipTitle = ARGUMENTS.Title />
  •  
  • </cfif>
  •  
  • <cfif Len( ARGUMENTS.PrivateKey )>
  •  
  • <cfset LOCAL.Parameters.SnipPK = ARGUMENTS.PrivateKey />
  •  
  • </cfif>
  •  
  • <cfif Len( ARGUMENTS.Owner )>
  •  
  • <cfset LOCAL.Parameters.SnipOwner = ARGUMENTS.Owner />
  •  
  • </cfif>
  •  
  •  
  • <!--- Submit the raw API request. --->
  • <cfset LOCAL.ResponseXML = THIS.SubmitRawSnipRequest(
  • "http://snipr.com/site/getsnip",
  • LOCAL.Parameters
  • ) />
  •  
  •  
  • <!--- Create the response structure. --->
  • <cfset LOCAL.Response = {
  • ID = LOCAL.ResponseXML.XmlRoot.ID.XmlText,
  • Title = LOCAL.ResponseXML.XmlRoot.Title.XmlText,
  • URL = UrlDecode(
  • LOCAL.ResponseXML.XmlRoot.URL.XmlText,
  • "utf-8"
  • ),
  • Created = ParseDateTime( LOCAL.ResponseXML.XmlRoot.Created.XmlText )
  • } />
  •  
  • <!--- Return API response. --->
  • <cfreturn LOCAL.Response />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="GetSnipDetails"
  • access="public"
  • returntype="struct"
  • output="false"
  • hint="I get details for the snip at the given ID.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="ID"
  • type="string"
  • required="true"
  • hint="I am the ID of the snip (URL will be stripped automatically if need-be)."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!--- Strip out any leading URL information to get the ID. --->
  • <cfset ARGUMENTS.ID = REReplaceNoCase(
  • ARGUMENTS.ID,
  • "^http://[^/]+/",
  • "",
  • "one"
  • ) />
  •  
  • <!---
  • Create the parameters collection and set required
  • values for API call.
  • --->
  • <cfset LOCAL.Parameters = {
  • SnipUser = VARIABLES.Instance.Username,
  • SnipAPI = VARIABLES.Instance.APIKey,
  • SnipID = ARGUMENTS.ID
  • } />
  •  
  • <!--- Submit the raw API request. --->
  • <cfset LOCAL.ResponseXML = THIS.SubmitRawSnipRequest(
  • "http://snipr.com/site/getsnipdetails",
  • LOCAL.Parameters
  • ) />
  •  
  •  
  • <!--- Create the response structure. --->
  • <cfset LOCAL.Response = {
  • ID = LOCAL.ResponseXML.XmlRoot.ID.XmlText,
  • Title = LOCAL.ResponseXML.XmlRoot.Title.XmlText,
  • URL = UrlDecode( LOCAL.ResponseXML.XmlRoot.URL.XmlText ),
  • Clicks = Val( LOCAL.ResponseXML.XmlRoot.Clicks.XmlText ),
  • Unique = Val( LOCAL.ResponseXML.XmlRoot.Unique.XmlText ),
  • Created = ParseDateTime( LOCAL.ResponseXML.XmlRoot.Created.XmlText ),
  • Modified = ParseDateTime( LOCAL.ResponseXML.XmlRoot.Modified.XmlText )
  • } />
  •  
  • <!--- Return API response. --->
  • <cfreturn LOCAL.Response />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="SubmitRawSnipRequest"
  • access="public"
  • returntype="xml"
  • output="false"
  • hint="I submit the raw HTTP POST and return the XML document.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="URL"
  • type="string"
  • required="true"
  • hint="I am the API action URL."
  • />
  •  
  • <cfargument
  • name="Parameters"
  • type="struct"
  • required="true"
  • hint="I am the set of parameters to pass to API."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  • <!--- Make CFHTTP POST request. --->
  • <cfhttp
  • method="post"
  • url="#ARGUMENTS.URL#"
  • useragent="SnipURLAPIBot"
  • result="LOCAL.APIResponse">
  •  
  • <!--- Loop over paramters to post. --->
  • <cfloop
  • item="LOCAL.Parameter"
  • collection="#ARGUMENTS.Parameters#">
  •  
  • <!--- Send through as form field. --->
  • <cfhttpparam
  • type="formfield"
  • name="#LCase( LOCAL.Parameter )#"
  • value="#ARGUMENTS.Parameters[ LOCAL.Parameter ]#"
  • encoded="false"
  • />
  •  
  • </cfloop>
  •  
  • </cfhttp>
  •  
  •  
  • <!--- Check the response for a successful connection. --->
  • <cfif NOT FindNoCase( "200", LOCAL.APIResponse.StatusCode )>
  •  
  • <!--- Throw error. --->
  • <cfthrow
  • type="SnipURL.SubmitRawSnipRequest.HTTPFailure"
  • message="The API request failed - #LOCAL.APIResponse.StatusCode#"
  • detail="The API request failed: #LOCAL.APIResponse.FileContent#"
  • />
  •  
  • </cfif>
  •  
  •  
  • <!--- Parse the resposne into XML. --->
  • <cfset LOCAL.ResponseXML = XmlParse(
  • Trim( LOCAL.APIResponse.FileContent )
  • ) />
  •  
  • <!--- Return XML document. --->
  • <cfreturn LOCAL.ResponseXML />
  • </cffunction>
  •  
  • </cfcomponent>

When you call the SnipURL.cfc methods, it submits the POST request and parses the XML response into easy-to-use ColdFusion structures. Here's a small demo that creates a shortened URL and then retrieves the URL details:

 Launch code in new window » Download code as text file »

  • <!--- Create the SnipURL API component. --->
  • <cfset objAPI = CreateObject( "component", "SnipURL" ).Init(
  • Username = "XXXXXX",
  • APIKey = "XXXXXXXX"
  • ) />
  •  
  • <!--- Create a snip. --->
  • <cfset objResponse = objAPI.CreateSnip(
  • URL = "http://flickr.com/photos/31062833@N07/3255353692/",
  • NickName = "dangy"
  • ) />
  •  
  • <!--- Output the response. --->
  • <cfdump
  • var="#objResponse#"
  • label="CreateSnip() Response"
  • />
  •  
  •  
  • <!--- Get the snip details. --->
  • <cfset objResponse = objAPI.GetSnipDetails(
  • ID = "dangy"
  • ) />
  •  
  • <!--- Output the response. --->
  • <cfdump
  • var="#objResponse#"
  • label="GetSnipDetails() Response"
  • />

When we run the above code, we get the following CFDump output:

 
 
 
 
 
 
Snurl.com (SnipURL) API ColdFusion Component Wrapper Response Structure. 
 
 
 

Nothing too complicated here, but maybe this will be useful to someone.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Other Searches  |  Print Page



Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Feb 5, 2009 at 1:05 PM // reply »
78 Comments

Don't forget to look over shrinkURL in case you ever want to access other services: http://snurl.com/anom6


Feb 5, 2009 at 1:08 PM // reply »
7,572 Comments

@Andy,

That is badass. I see it even covers Snurl! Perhaps I will just skip my integration and move to yours directly.


Feb 5, 2009 at 1:21 PM // reply »
78 Comments

Snurl is the service that I personally use. It's quick, been around for a while, and offers built in tracking for URLs. Those are the three main requirements for me personally.

I hope you like it Ben...it was something I came up with so that I could integrate into the blog software that I'm writing. My original goal was to write a blog post then, when published, the software would create a short URL from a selected service, then submit a tweet about it using the Twitter API.

Adam Tuttle over at Fusiongrokker.com has submitted several changes and additions so it's been seen by more than just my eyes. Please let me know if you've got any suggestions. I'm hoping to let this get some good exposure and your software would definitely help it do that.


Feb 6, 2009 at 9:03 AM // reply »
16 Comments

Indeed, it's pretty well tested at this point. And I've submitted some changes to make it compatible with CF 6, and 7, as well as Railo. :)


Feb 6, 2009 at 9:40 AM // reply »
78 Comments

I'll be taking Adam's Railo compatibility changes, adding some additional services like URLZen and Zi.ma, and releasing shrinkURL 0.4 this weekend.


Feb 6, 2009 at 9:43 AM // reply »
7,572 Comments

@Andy,

I haven't looked at the actual code yet - are you building all the services into one CFC? Or is there like an abstract CFC that the different services extends (like Snul.cfc extends ShrinkUrl.cfc)?


Feb 6, 2009 at 10:09 AM // reply »
78 Comments

All services in one CFC.

Currently there's a controller method "shrink" which calls a single method for each service, each of which in turn reference a master "callAPI" method.

There's a structure in the CFC's variables scope containing meta data about each of the services. This metadata can be exposed by calling the listServices method.

I've got plans to remove the unique methods for each service, essentially building a method at runtime based on the metadata for that service.


Feb 6, 2009 at 10:19 AM // reply »
16 Comments

Andy, I'm not sure if I'm misunderstanding your intentions, but wouldn't it be easier just to write a function that uses the configuration from the services structure to compose the API call as needed? You may need to add a configuration point or two (ReturnsXML = true/false, comes to mind), but it doesn't sound too difficult. :)


Feb 6, 2009 at 11:00 AM // reply »
78 Comments

That's pretty much exactly what I'm talking about. One method which uses the metadata from the services structure to make the call.


Post Comment  |  Ask Ben

Recent Blog Comments
Mar 22, 2010 at 3:08 AM
Ask Ben: Selecting XML Attributes Given Other XML Attributes
Thanks for the response. I finally discovered that I was getting this error because I had cfsetting enablecfoutputonly="yes" in Application.cfc, and was neither setting it to false elsewhere nor brac ... read »
Mar 21, 2010 at 8:57 PM
The Bourne Ultimatum Starring Matt Damon And Julia Stiles
late to the party, but my observation is this: rewatch carefully for the platonic nature of the relationship between nicki and jason. she never flirts with him. he never comes on to her. they alway ... read »
Mar 21, 2010 at 7:40 PM
Is Simulating User-Input Events With jQuery Ever A Good Idea?
A couple of things. One you embed the initial state of of more-info in the CSS. IMHO, that behavior should be in jQuery: moreInfo.hide(); It shows that the behavior your toggling and closing is mor ... read »
Mar 21, 2010 at 3:59 PM
Exploring ColdFusion Component Runtime Class Properties And Serialization
@Elliott, according to Ben's experiment, serializeJSON() doesn't access the private data by default - it doesn't even access the getHair() method - so trying to clone a Girl.cfc via serializeJSON/des ... read »
Mar 21, 2010 at 3:49 PM
Ask Ben: Javascript String Replace Method
I'm confused a bit by what you are asking, but if had this sentence: The color, red, is in the style statement; style: red;. and wanted to remove all or change all of the commas, colons, and semi-c ... read »
Mar 21, 2010 at 3:13 PM
Ask Ben: Javascript String Replace Method
I am trying to make a java program to count the number of times that these punctuation marks occur in a body of text: , : ; . ! - ' " ? / \ I am using this piece to ferret out the commas: numcommas ... read »
Mar 21, 2010 at 11:13 AM
A New Wrist Pain
@chiropractor suwanee, Spoken like someone trying to sell something. Other than for minor, temporary relief from some back pain, chiropractic treatment is nothing but placebo effect and quackery. ... read »
Mar 21, 2010 at 6:32 AM
ColdFusion CFPOP - My First Look
Apologies... The field name in the db for C. is "BounceCode" It stores the code / message which is returned in the email. Sorry for the confusion. ... read »