JSAPI

Hackdays: Playgrounds for the Imagination

Posted in API, Geek Stuff, JSAPI, LinkedIn on July 13th, 2011 by admin – Be the first to comment

One of the best things about working as an engineer at LinkedIn is the monthly hackdays. This is an amazing opportunity that feels a lot, to me, like playing the lottery – it’s a time I can set aside to let myself imagine the possible, to think about what people might want to do, play with, or learn.  Think of better ways to accomplish the things that frustrate me, or to share things I love with other people.  Individuals and teams of LinkedIn employees take the day (and sometimes the weekend afterwards) to create mockups, prototypes, and sometimes complete applications to demonstrate the ideas bouncing around in their head.  Our company benefits by getting tons of new ideas, and our engineers benefit because… well, because play is fun, and this is play.  And we get to present our ideas to the executive team, and earn Apple gift cards for our efforts.

Not wanting to keep all this fun to ourselves, on July 29 LinkedIn will be hosting our first ever Intern Hackday – we’re offering cool prizes and free food, and the opportunity to collaborate with a bunch of other inspired and inspirational interns for 24 hours.

But that’s not all!  The open source developers among you know that OSCON is coming up at the end of the month, and Jeremy Johnstone and I will be there all week talking to developers and helping them started with the LinkedIn platform.  We’ve just teamed up with Mashery, Urban Airship, and a host of other fantastic companies to help put together the API Hackday PDX on Saturday, July 30.  I’ll be there to give a presentation on our APIs and help people with LinkedIn platform questions, and LinkedIn is going to give 3 $100 Apple Gift Cards to the best applications using our platform – whether it be brand new applications, multi-platform mashups, or integrations into existing systems. Come show us what you’ve got, get some LinkedIn shwag, learn about our platform and win some Apple dough!

If you want to get a jump on the competition, head over to our Developer Portal and start playing with our APIs. We’ve got a Javascript API and a REST API, along with some plugins you can use for quick integrations.  We’ve got cool tutorials, documentation, and a very active community in our forums.

Here are some links to code walkthroughs you might find helpful:

    Posted in API, Geek Stuff, JSAPI and LinkedIn here.  Posted in Friends and Open Source in G+.

    Browsing your LinkedIn Connections by Industry

    Posted in JSAPI, LinkedIn, Uncategorized on March 9th, 2011 by admin – 3 Comments

    I presented at the Semantic Web Meetup in San Francisco about this topic, slides for that talk can be found here.

    A friend of mine, who runs a rather cool website, asked me to help him understand how to create an industry-based query to see which of your connections are in a particular industry.  As this was a slightly trickier-than-usual problem, I put together an example for him to use, and I’ll go over how it works here.

    But first, the obligatory working demo:

    And the code. Note there are a couple of support files you’ll need to snag as well.

    In order to keep this post manageable, I’ll mention that most of the pieces for this are included in my StreamIn’ tutorial. So if you’re interested in understanding how the basic framework works, take a look there. We’re basically going to break out on “Add Share Stream”.

    Industries

    One of the reasons that this example is somewhat tricky is that the API doesn’t currently have a way to give you a list of industries, and how they’re related, but we do have a web document (which is fairly static) with that information. So I grabbed the HTML from that page, fiddled with it in my editor, and created a JSON document with a hash of industries (and the groups they map to) and groups (and the industries they contain). You can see that file here. Loading that file as a javascript script gives the page access to hashes of this information.

    Autocomplete

    Hey, since we’ve made this handy hash, it’s a cinch to provide a jQuery autocomplete for the industries.

    	var data = Object.keys(industry_names);
    	$("input#Industry").autocomplete({
    	   source: data,
    	   select: function() { getConnections() }
    	});

    Titles is not really a bounded set of this type, so I’ll just let the user input that freestyle.

    Getting the connections

    The logic I wanted to use was… find all my 1st or 2nd degree connections in the industry who match the title. If there are fewer than 25 of those, do another search for all of the industries in the industry group. You can see how that stacks in the code, so I’ll just talk through the actual call for the first search.

    	$("#connections").html("");
    	var industrynum = industry_names[$("#Industry").val()]["code"]
    	var title = $("#Title").val();
    	path = "/people-search:(people:(id,first-name,last-name,public-profile-url,
                      three-current-positions:(title,company:(name,industry)),distance,picture-url))?
                      sort=distance&facet=industry," + industrynum + "&title=" + title + "&current-
                      title=true&facet=network,F,S&count=25"

    So, clear the div we’re gong to use. Grab the code from the industry hash based on what’s in the Industry user input field. Pull the title from the Title input field.

    In this case we’re using the People Search resource with some field selectors to request specific profile fields for the users who are returned. That’s this part:

    people-search:(people:(
                         id,first-name,last-name,public-profile-url,
                         three-current-positions:(title,company:(name,industry)),
                         distance,picture-url))

    Why three-current-positions? Because I can’t just ask for one current position, and this list will be smaller and faster than all the positions.

    We also need some very specific filters on our search, so we’re using facets (also covered on the People Search page) to get us only the members in our network who have the right title and industry. Let’s go over those things individually:

    • sort=distance : distance from me, i.e. degree (first or second)
    • facet=industry,xx : I want only users who are working in this industry (like Entertainment or Internet)
    • title=yyy : This is a fuzzy string match. Doesn’t have to match exactly.
    • current_title=true : Don’t tell me about their past positions. I want people doing this right now
    • facet=network,F,S : First and Second degree connections only.
    • count=25 : I don’t have all day and I’m only going to show 25 total results in any case.

    All right, now that that’s settled, let’s make the call. The Raw API call we’re making reaches directly through to the back end REST API.

    	IN.API.Raw(path)
    	  .result(function(result) {
    	    var count = result.people.values.length;
    	    var html = $("#connections").html();
    	  	for (var index in result.people.values) {
    	  		var person = result.people.values[index]
    	  		html += "<div class=\"connectionitem\">";
    	  		if (person["pictureUrl"]) {
    		  		html += "<img src= \"" + person["pictureUrl"] +
                                                  "\"  width=40 align=right/>";
    			}
    			var distance = (person["distance"] === 1 ? '1st' : '2nd')
    	  		html += "<a href=\"" + person["publicProfileUrl"] + "\"><h3>" +
                                          person["firstName"] + " " + person["lastName"] +
                                          " (" + distance + ")</h3></a>";
    	  		html += person["threeCurrentPositions"]["values"][0]["title"] +
                                        ", " + person["threeCurrentPositions"]["values"][0]["company"]["name"] +
                                       " (" + person["threeCurrentPositions"]["values"][0]["company"]["industry"] +
                                       ") </div>";
    	  	}
    	  	$("#connections").html(html)
    	  });

    Just as in the other tutorials, the JSAPI method (in this case IN.API.Raw()) is called with chained methods indicating how it should be executed. In this case, we just want to perform a GET on the resource, and process the results, so .results is the only thing we’re setting.

    Just like in the StreamIn’ tutorial, the results are iterated over person by person and a collection of connectionitems is gathered together, made pretty, and plopped in the “connections” div.

    Creating an Application using LinkedIn Platform in 3 Easy Steps!

    Posted in API, JSAPI, LinkedIn on February 17th, 2011 by admin – 2 Comments

    The LinkedIn API has all sorts of juicy goodness to it, but the authentication and structure take some learning. LinkedIn has created a JS API called Connect to help you get around those things, so that you can focus on the logic and presentation of your application without spending too much time considering the back end.

    I made an internal presentation covering this material at LinkedIn on 2/18, which is available in PDF format. This tutorial has also been integrated into the general JSAPI tutorials on the developer portal. That version has a lot less narration and is just about getting the code written and working.

    Everyone loves activity streams, so to illustrate the application development process I’m going to walk through the steps needed to create an interactive LinkedIn application. StreamIn’ has a profile badge for the logged in member, a stream of the status updates from members in their network, and the ability to like/unlike particular items.

    A note on this tutorial – there is detailed documentation for the JSAPI on the LinkedIn Developer Portal – there are links in the text appropriate, but detailed options aren’t covered here to keep the tutorial reasonably sized. It’s a lot more fun to get started in a framework if you’ve built something from start to finish, and that’s what we’ll do here.

    Getting Started

    To get started on our exercise, we need a shell for the application.  Here we have a basic layout, with places for the badge and stream to be inserted.

    <div id="header"><h1>StreamIN'</h1></div>
      <div id="wrapper">
        <div id="profile">
    	    <div id="badge">
    	    Profile info goes here!
    	    </div>
        </div>
        <div id="stream">
            Stream stuff goes here!
        </div>
        <br clear="both">
        <div id="footer">
        <div id="nav"> [ << ] <a href="Step1.html">[ >> ]</a></div>
    </div>

    We’ll start with this and build the application in stages.

    Step 0: Check out the Console

    The Connect framework does all of the authentication work for you, and loads the things you need in order to get things working in your application.  There are various ways to present login options for the member, and you can explore them using the new JSAPI Console.  At the very least you’ll want to explore the Login options (Login Button, Login Button with Events, Login Button Label). The console is an incredibly useful tool!  Take a few moments now to look around and see what it has to offer.  You can even edit the Code section and click “Run” to see how the Results change.

    Step 1: Get the Profile

    In order to show the user’s profile, we’ll need to do a few things.

    • Import the framework
    • Add a login button
    • Add an API call to get the profile data and display it

    I know, that seems like a lot of sub-steps, but it’s really very easy.

    Import the Framework

    The first thing we need to do when using Connect is get the framework in our page.  In the <head> of the document, we’ll add a small script to grab the framework.  To do this you need to get a LinkedIn API key – if you don’t have one, go ahead and get one and then come back – it doesn’t take long and it’s much more fun to play along than just read about what I did.

    Here’s your code. Use your own api_key. “authorize:true” tells the framework to authorize the member if they’ve visited your application before, rather than logging in each time.

    <script type="text/javascript" src="http://platform.linkedin.com/in.js">
       api_key: API_KEY
       authorize: true
    </script>

    Add a Login Button

    Let’s add the button at the bottom of the page.  Here’s the code.  No really, this is all there is!  The data-onAuth variable tells the framework to fire the loadData function when the member has been authorized.

    <script type="IN/Login" data-onAuth="loadData"></script>

    Load the User’s Profile

    Here’s the meat of the step – now the framework is in place, so it’s possible to make calls on behalf of the member. When the member has been authorized the loadData() function will be called. This function calls the Profile method. We need a couple of extra fields from that call, so we use .fields to tell the call what to request.  And the .result sets a callback to perform as soon as the call returns.  You can put an anonymous function there (as I have here) or give it the name of another function to process your returned data.

    The callback in this case just pulls the user’s information from the result and builds a profile badge, inserting it in the badge <div>.

    function loadData() {
    // we pass field selectors as a single parameter (array of strings)
    IN.API.Profile("me")
       .fields(["id", "firstName", "lastName", "pictureUrl","headline"])
       .result(function(result) {
          profile = result.values[0];
          profHTML = "<p><a href=\"" + profile.publicProfileUrl + "\">";
          profHTML +=  "<img align=\"left\" src=\"" + profile.pictureUrl + "\"></a>";
          profHTML +=  "<a href=\"" + profile.publicProfileUrl + "\">";
          profHTML +=  "<h2>" + profile.firstName + " " + profile.lastName + "</a> </h2>";
          profHTML += "<span>" + profile.headline + "</span>";
    $("#badge").html(profHTML);
    });
    }

    This is how that renders – and you can look at the code for this step.

    Step 2: Add Share Stream

    The member is authorized and we can see their information. The next thing the application needs to do is grab all of the status updates in their network stream and insert the information into the application.

    This can be done using the NetworkUpdates function. To restrict the type of update to “STAT” (Status) updates, .params is used. For simplicity we’re using another inline function, which iterates over the updates and builds individual stream items for each one, then injects the html into the stream <div>.

    There also needs to be a call to getUpdateStream() in the loadData() function so it gets called after the profile is loaded.

    function getUpdateStream() {  
    	IN.API.NetworkUpdates()
    	.params({type:"STAT"})
    	.result(function(result) {
    	    var streamHTML = "";
    		for (var update in result.values) {
    			var thisupdate = result.values[update]
    		
    			// Build each individual stream update item
    			person = thisupdate.updateContent.person
    			var thisHTML = "<div class=streamitem>";
    			
    			// Person's picture,  linked name, and status
    			thisHTML += "<img align=\"left\" class=img_border height=\"50\" src=\"" + person.pictureUrl + "\"></a>"; 
    			thisHTML += "<a href=\"" + person.publicProfileUrl + "\">";
    			thisHTML += "<span class=updater>" + person.firstName + " " + person.lastName + "</span></a>";			
    			thisHTML += "<p class=update>" + activateLinks(person.currentStatus) + "</p>"
    			thisHTML += "</div>";
    			streamHTML += thisHTML
    		}
    		$("#stream").html(streamHTML);
    	});
    }
    

    And now we have this. Code is here.

    Step 3: Adding Interaction

    Reading from the API is all well and good, but it’s pretty hard to make a compelling application if the member can’t do anything with it. Status updates can always be “liked”, so let’s add some like/unlike action to StreamIn’.

    This part is a little more complicated. getUpdateStream() needs to be extended to add Like/Unlike buttons, and those buttons have to perform the appropriate action when pressed.

    The Connect calls for these buttons are a little trickier. Since there is no Like/Unlike convenience method in Connect, we’ll pass through a raw call to the backend API. For this call, the method needs to be “PUT”, and the body is simply “true” or “false”. There’s an “alert” here to tell the user something happened, and then the stream is reloaded and redisplayed.

    Here’s getUpdateStream again, with the newly added code in bold. The first section simply adds a button to the stream item – if the user has not yet liked the item, a Like button is shown. Otherwise, there’s an Unlike button.
    The other two functions are event handlers for clicks on these buttons.

    
    function getUpdateStream() {  
    	IN.API.NetworkUpdates()
    	.params({type:"SHAR"})
    	.result(function(result) {
    	    var streamHTML = "";
    		for (var update in result.values) {
    			var thisupdate = result.values[update]
    		
    			// Build each individual stream update item
    			person = thisupdate.updateContent.person
    			var thisHTML = "<div class=streamitem>";
    			
    			// Person's picture,  linked name, and status
    			thisHTML += "<div class=updateperson>" ;
    			thisHTML += "<img class=img_border align=\"left\" height=\"50\" src=\"" + person.pictureUrl + "\"></a>"; 
    			thisHTML += "<a href=\"" + person.publicProfileUrl + "\">";
    			thisHTML += "<span class=updater>" + person.firstName + " " + person.lastName + "</a></span>";	
    			thisHTML += "<p class=update>" + activateLinks(person.currentShare.comment) + "</p></div>";
    						
    			// Present a like button
    			if (! thisupdate.isLiked) {
    				thisHTML += "<div id=button><button class=\"likebutton ui-corner-all\" id=\"" + 
                                    thisupdate.updateKey + "\"><img src=\"Thumbs_up.png\"> Like</button></div>"
    			} else {
    				thisHTML += "<div id=button><button class=\"unlikebutton ui-corner-all\" id=\"" +
                                    thisupdate.updateKey + "\"><img src=\"Thumbs_down.png\"> Unlike</button></div>"
    			}	
    			thisHTML += "</div>";
    			
    			// Slap this onto the HTML we're building
    			streamHTML += thisHTML;
    		}
    		$("#stream").html(streamHTML);
    	});
    	
    	$( ".likebutton" ).live("click", function() {
    		   likeURL = "/people/~/network/updates/key=" + $(this).attr("id") + "/is-liked"
    		   IN.API.Raw(likeURL)
    			.method("PUT")
    			.body("true")
    			.result(function(result) {
    				alert ("Liked");
    				getUpdateStream();
    			})
    	});	
    	
    	$( ".unlikebutton" ).live("click", function() {
    		   likeURL = "/people/~/network/updates/key=" + $(this).attr("id") + "/is-liked"
    		   IN.API.Raw(likeURL)
    			.method("PUT")
    			.body("false")
    			.result(function(result) {
    				alert ("Unliked");
    				getUpdateStream();
    			})
    	});	
    }
    

    So that’s it. The code for Streamin’ code is here. Now that you’ve gotten an app up and running, check out the docs and see what you can build!

    Using Faceted Search from the LinkedIn API

    Posted in API, JSAPI, LinkedIn, YUI on January 31st, 2011 by admin – 4 Comments

    As many of you know, I’m now happily employed as a Developer Advocate at LinkedIn. Part of my job there is to create interesting examples using our APIs, to help other developers find their footing and create new and interesting applications.  This will be the first in a long line of posts demonstrating how to use our API to create cool apps.

    Note that this, as with many other applications, will be much less interesting with small networks. Grab more connections and it’ll have more cool data to play with.

    Faceted Search


    My passion has always been for showing interesting intersections of data.  The LinkedIn website has a faceted search feature you can see when browsing people you may know. On the left hand side of that page, you’ll see companies and schools listed there.  Selecting one or more companies restricts the people to those folks who match one of those companies.  Selecting a school or schools does the same thing.  Selecting both creates an “and”.

    Try these examples on the website to get a feel for how the facet searches work. Because these facets are tuned to your network, you’ll have different companies and schools, but you can still see how the intersections work.

    • People who worked at Netflix in the past
    • People who worked at Netflix in the past *and* attended Stanford University
    • People who worked at Intel in the past and work at Yahoo! now
    • People who worked at either Cisco Systems or Microsoft in the Past

    This same functionality is available via the LinkedIn API. Information on faceted search can be found in the People Search API documentation.

    FacetBrowse

    So how does this translate into a real application? The application above shows one possible implementation. But how does it work?  I’ll walk through the pieces of this application.  If you want to see the source application (which uses the LinkedIn JSAPI for interaction with LinkedIn, and YUI for presentation) you can look at the page directly at http://www.princesspolymath.com/facetbrowsesmall.html

    On the left hand side of the application you see a list of buttons – by default you’re looking at the companies, but go ahead and switch to schools. This list is built as soon as the user has authenticated with LinkedIn, with a raw query to the API:

    function onLinkedInAuth() {
        IN.API.Raw("/people-search:" +
       "(people:(first-name,last-name,positions:" +
        "(company:(ticker,name)),educations,picture-url),facets:" +
        "(code,buckets:(name,code,count)))?" +
        "facets=current-company,school&sort=distance&count=25")
        .result(displayFacetResults)
    }
    

    This returns the top 10 schools in your network, the top 10 companies in your network, and 25 of your contacts. FacetBrowse builds the buttons for the left hand side of the widget and labels them so the app knows what’s been selected when someone clicks one of the buttons. It then trims out contacts who don’t match any of the top companies or schools, leaving you with something that looks like this:

    Any time one of the buttons is clicked, the function refreshConnections is called, which does the same facet search – this time specifying a company and/or school.

    function onLinkedInAuth() {
        IN.API.Raw("/people-search:" +
       "(people:(first-name,last-name,positions:" +
        "(company:(ticker,name)),educations,picture-url),facets:" +
        "(code,buckets:(name,code,count)))?" +
        "facets=current-company,school&sort=distance&count=25" + 
        "facet=past-company,{company-code}&facet=school,{school-code})
        .result(displayFacetResults)
    }

    Note that because of the way the facet search works, you can’t create intersections within the request (this company *and* that company). Since I want to create these intersections, when I’m refreshing connections, I ask for one of the companies and one of the companies and then eliminate matches which don’t match all of the company/school choices.

    For instance, when the user selects two different companies, such as they have here:

    The application asks for the people-search and declares “current-company=“. When the results come back, each person is inspected to make sure that they have both Microsoft and Netflix within their positions. Matching results are displayed in the grid.

    If a school is then selected (so now we have Stanford, Microsoft and Netflix):

    The query requests Stanford University as a school and Microsoft as a company, and then checks each returned person to make sure they also have Netflix in their profile.

    This application is just a small example of what can be done with the LinkedIn faceted search. Tell me in the comments if you’ve found other uses for it, or have questions about how it works.