Saturday, June 18, 2016

Ridiculously Easy Autocomplete with the EveKit Online SDE

If you don't know already, here at Orbital Enterprises we host the two most recent versions of the EVE Online Static Data Export as a web service.  I posted about this previously here.

As I mention in the original blog post, the online SDE is mostly useful for short queries or lookups, and isn't the best thing for complicated queries (e.g. things that would end up being complicated joins if you had direct access to the database).  However, for web developers out there, it turns out to be ridiculously easy to implement nice features like EVE type name auto-completion using the online SDE.  I'll show you how to do that using javascript in this blog entry.

Resources

EveKit SDE Public Site
GitHub Project Page
Sample Code

Annotated Example

You can download the sample file (auto.html) from the link above.  You should be able to point your browser to a local copy of the sample and auto-complete EVE type names in the text box.  We'll go over the important parts of the sample file below.

We start out by loading a few important libraries in the header:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>autocomplete demo</title>
  <link rel="stylesheet" href="https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
  <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
  <script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
  <script src='https://cdn.rawgit.com/swagger-api/swagger-js/master/browser/swagger-client.min.js' type='text/javascript'></script>
</head>
<body>

We use jQuery's auto-completion support to drive our flow.  You don't have to use jQuery to do this; there are other javascript libraries that support auto-completion.  In fact, all you really need is something that will accept text and periodically query an auto-completion routine to return possible choices.  But jQuery is one of the easiest to use, so that's what we use here.

We also load the Swagger javascript client library.  This library will turn the online SDE API spec into a usable javascript client.  We talk about Swagger all the time with EveKit because all our APIs are annotated with Swagger, making it very easy to build clients for our tools.  See some of our other posts for more detail on this topic.

There are two important bits of code in the sample file.  The first is the code which builds the Swagger client:

var url = "https://evekit-sde.orbital.enterprises/20160531/api/ws/v20160531/swagger.json";
var swagger = new SwaggerClient({
    url: url,
    usePromise: true
}).then(function(obj) {
    // save a debug copy of swagger as well as returning
    window.swagger = obj;
    console.log("swagger ready");
    $( "#typecomplete" ).autocomplete({ source: typeRequester });
    return obj;
}).catch(function(error) {
    console.error('Swagger promise rejected', error);
    return null;
});

The SwaggerClient class comes from the Swagger javascript library and is parameterized with a URL pointing to the location of the online SDE API specification.  In this example, we're using the most recent SDE at time of writing.  This will change over time, so be sure to visit the SDE page on a regular basis to get the latest URL.

The calling style used here is based on "promises" (and is initiated by passing the "usePromise: true" flag to Swagger).  Promises are a very clean approach to handling the asynchronous nature of remote calls in javascript.  You can read a very accessible introduction to promises here.  But even if you've never seen promises before, it's easy to intuit how the code works here: first we create the SwaggerClient object with an appropriate URL; THEN we save the result of that call and initialize the type completer; if something goes wrong, we CATCH the error and do something appropriate.

So the net result of this call is that we'll have an initialized text box and a Swagger client ready to retrieve data from the online SDE.  When the auto-completer is ready to attempt a completion, it calls the "typeRequest" function with the text the user has typed so far.  This is where we add the code to look up potential completions from the online SDE:

/* Support autocomplete of an EVE type */
var typeRequester = function(request, response) {
    // Query term uses SQL wildcards
    var queryTerm = '%' + request.term + '%';
    // Limit to 100 results for speed
    swagger.Inventory.getTypes({maxresults: 100, typeName: '{like: "' + queryTerm + '"}'})
.then(function(result) {
   var choices = [];
   for (var i = 0; i < result.obj.length; i++) {
choices.push(result.obj[i].typeName);
   }
   response(choices);
}).catch(function(error) {
   console.error("Swagger promise rejected", error);
   response([]);
});
};

According to the jQuery docs, the auto-completer will pass a "request" object with a "term" property containing the text entered by the user.  Any potential choices should be returned by invoking the "response" object, which will be a callback taking a list of choices.

To lookup potential matches, we use the "like" query support provided by the online SDE.  You can read more about this feature (and other query choices) here.  Essentially, we're constructing a SQL wildcard match of the form '%term%' and invoking the "getTypes" endpoint to collect potential matches.  We use the "promise" calling style again and populate an array of choices based on the returned type names.  If we catch an error, we return an empty list (the jQuery library requires that we always return a result, even if an error occurs).  We also limit the number of responses to make the auto-completion appear a bit snappier.

Parting Words

That's all there is to it!  The same pattern is usable with other SDE calls as well.  For example, you can create a "region auto-completer" by replacing "swagger.Inventory.getTypes" with "swagger.Map.getRegions" (and other naming changes, like replacing "typeName" with "regionName").  Hope this helps.  Happy coding!

0 comments:

Post a Comment