Notes on migrating an application from AppJet to Smart Platform

I’ve been wingeing quite a lot recently about AppJet’s demise and the shoddy state of potential replacements, and got quite a nice response from Jim Pick at Joyent. I decided today to have a play with their new Smart Platform and had a go at porting one of my AppJet applications over to Smart. Here’s my notes, fresh from the lab…

It’s not as simple as hitting an edit button yet, but it’s still worth having a play. The use of Sammy to handle and dispatch incoming requests, along with a single “bootstrap.js” file that controls all the SSJS, means that it ought to be fairly easy to port over single-page AppJet apps to Smart Platform. Here’s some notes I’ve gathered whilst porting over an application that acts as a gateway between tarpipe’s REST connector and a Yahoo! Pipe, allowing you to send data to a Yahoo! Pipe for processing.

Request object

In AppJet, you get the POSTed variables and the query-string parameters both in request.params. In Smart, the query-string parameters are in request.query with the raw string in request.queryString; POSTed variables are in request.body with the raw datastring in request.content.

Dispatching

In AppJet, you have the option to define different functions for handling different types of request e.g. GETs, POSTs and requests to different resources e.g. /this, /that. Smart uses Sammy, which gives you a similar mechanism to choose which functions respond to different incoming requests, although you also have the option to use a single function main(request) {...} to handle all responses. A concrete example is:

AppJet:
    function get_resource() { ... }
Smart:
    GET("/resource", function() { });

Writing a response

AppJet provides a default chrome that wraps an application, which you can remove by setting page.setMode('plain'); then you use functions like print(response) to send the respons. Smart does it slightly differently, where whatever you return from a function is sent as the response. It doesn’t seem possible to send HTTP header information.

Making HTTP requests

AppJet provides functions like wpost and wget to make HTTP calls. Smart provides the system.http.request function to do the same sort of thing. The difference in the returned response is that AppJet gave you a raw string of the response body, whereas Smart returns an object containing headers and content properties.

Deployment

The lack of an in-browser edit experience has been my complaint of late, but doing the Git bit to deploy my app to Smart was straightforward, and now it’s up at http://f7e3dbf.smart.joyent.com.

Try it out with:

curl -d 'pipeId=lAUEoB1R3hGTqt6hggSecQ&data={"items":[{"title":"A title","description":"Any description","link":"http:\/\/example.com"}]}' http://f7e3dbf.smart.joyent.com

Code

For the sake of completion, here’s the source code. I’ve gone from this:

/* appjet:version 0.1 */
import("lib-json"); 

var p = request.params;
var result = "";
var output = ""; 

function serialize(obj) {
    var str = "";
    for(var i in obj) {
        str += "&"+encodeURIComponent(i)+"="+encodeURIComponent(obj[i]);
    }
    return str.substring(1);
}
if(p && p.data && p.pipeId) {
    var data = JSON.parse(p.data);
    var pipeId = p.pipeId;
    var items = data.items;
    if(items) {
        var url = "http://pipes.yahoo.com/pipes/pipe.run?_id="+pipeId+"&_render=json";
        // tarpipe only POSTs 1 item
        var result = wpost(url,{title:items[0].title,description:items[0].description});
        if(result) {
            result = JSON.parse(result);
            if(result) {
                output = result.value;
                if(!output.items[0].title) {
                    output.items[0].title = "returned from Yahoo! Pipe at: "+pipeId;
                }
                output = JSON.stringify(output);
            } else {
                result = "bag result from Yahoo! Pipe at: "+pipe;
            }
        } else {
            result = "no result returned";
        }
    } else {
        result = "no items in data input";
    }
} else {
    result = "no data or no pipe input";
} 

page.setMode('plain');
output ? print(output) : print(result);

To this:

system.use("com.joyent.Resource");
system.use("org.json.json2");

function serialize(obj) {
    var str = "";
    for(var i in obj) {
        str += "&"+encodeURIComponent(i)+"="+encodeURIComponent(obj[i]);
    }
    return str.substring(1);
}

function main(request) {
	var p = request.content!="" ? request.body : request.query;
	var result = "";
	var output = "";
	/* for debug input
	for (var i in p) {
		output += i+", "+p[i]+"\n";
	}
	return output;*/
	if(p && p.data && p.pipeId) {
	    var data = JSON.parse(p.data);
	    var pipeId = p.pipeId;
	    var items = data.items;
	    if(items) {
	        var url = "http://pipes.yahoo.com/pipes/pipe.run?_id="+pipeId+"&_render=json";
	        // tarpipe only POSTs 1 item
	        var toPost = "title="+items[0].title+"&description="+items[0].description;
	        var result = system.http.request("POST",url,null,toPost);
	        if(result && result.content) {
	            result = JSON.parse(result.content);
	            if(result) {
	                output = result.value;
	                if(!output.items[0].title) {
	                    output.items[0].title = "returned from Yahoo! Pipe at: "+pipeId;
	                }
	                output = JSON.stringify(output);
	            } else {
	                result = "bag result from Yahoo! Pipe at: "+pipe;
	            }
	        } else {
	            result = "no result returned";
	        }
	    } else {
	        result = "no items in data input";
	    }
	} else {
	    result = "no data or no pipe input";
	}
	return output || result;
}
Advertisements

2 Comments

  1. Posted July 16, 2009 at 5:26 pm | Permalink

    Just a quick note regarding headers – it is definitely possible to set them.

    If you’re using Sammy simple set a key/value pair in this.response.headers from within a handler. If you’re working further down the stack simply include it in the returned http response.

    (http://smart.joyent.com/docs/basics.html explains the format)