Fun with Outbound REST Events, Part IV

“Life is trying things to see if they work.”
Ray Bradbury

We got a little side-tracked last time, but we are finally ready to tackle the Script Include that will utilize our new Outbound REST Message. I like to tackle things a little bit at a time and test, so we will start out with just the code to invoke the service and format a response to the caller, and then deal with all of the error handling later on in the process. We will also want to call this from a Client Script, but again, let’s not worry about that part just yet and focus on successfully launching the service and interpreting the results.

To start with, we pull up the list of Script Includes and hit the New button to create a new script. We will call our new script AddressValidationUtils and create a function called validateAddress that takes street, city, state, and zip as arguments. The first thing that we will want to do in our new function will be to establish the object that will be returned to the caller.

var response = {result: 'failure', street: street, city: city, state: state, zip: zip};

Our result property will have three possible values: valid, invalid, or failure. We initially default it to failure, mainly because there will be a lot places where we might have to set it to failure, and then alter it to one of the other two values when appropriate. The remaining properties are defaulted to their original value, and if the address is deemed valid, will be updated with the values returned by the service to clean up any inconsistencies. Now let’s add the code to invoke the service.

var rest = new RESTMessage('US Street Address API', 'get');
rest.setStringParameter('authid', gs.getProperty('us.address.service.auth.id'));
rest.setStringParameter('authToken', gs.getProperty('us.address.service.auth.token'));
rest.setStringParameter('street', encodeURIComponent(street));
rest.setStringParameter('city', encodeURIComponent(city));
rest.setStringParameter('state', encodeURIComponent(state));
rest.setStringParameter('zip', encodeURIComponent(zip));
var resp = rest.execute();

This is all pretty straightforward: we establish the rest object by creating a new RESTMessage using the name of the message and the name of the method. Once the object has been established, we use the setStringParameter method to set the value for each of the required variables. Once again, the system does not encode any of the values, so we need to do that for any user input. We don’t need to do that for any of the credentials, as we already know the value of those and we know that there are no characters in either of those that would cause an issue with the URL. Once all of the variable value have been set, we then make the call to the service using the execute method.

Now we need to add code to interpret the response.

var body = resp.getBody();
if (resp.getStatusCode() == 200) {
	var respArray = [];
	try {
		respArray = JSON.parse(body);
	} catch (e) {
		gs.info('Exception: ' + e);
	}
	if (respArray && respArray.length > 0) {
		respObj = respArray[0];
		if (typeof respObj.analysis == 'object') {
			var validity = respObj.analysis.dpv_match_code;
			if (validity == 'Y' || validity == 'S' || validity == 'D') {
				response.result = 'valid';
				response.street = respObj.delivery_line_1;
				response.city = respObj.components.city_name;
				response.state = respObj.components.state_abbreviation;
				response.zip = respObj.components.zipcode;
				if (respObj.components.plus4_code) {
					response.zip += '-' + respObj.components.plus4_code;
				}
			} else {
				response.result = 'invalid';
			}
		} else {
			gs.info('Invalid Response Object: ' + JSON.stringify(respObj));
		}
	} else {
		gs.info('Invalid Response Body: ' + body);
	}
} else {
	gs.info('Invalid HTTP Response Code: ' + resp.getStatusCode());
}

There is a little more code here, but most of it is defensive, just to make sure that we don’t choke on something unexpected. All of the gs.info statements are just placeholders at this point; eventually we will replace those with Event logging, but I’m not ready to deal with that just yet, so we’ll put that off for now and just write to the system log. Assuming all goes well, we will have the response object in hand, and then all we have to do is check to see if it validated or not. If it did, we pass back their version of four address components, and if it didn’t, then we just indicate that is invalid.

Once get through all of that, all that is left is to return the response to the caller. Here’s the whole thing, all put together.

var AddressValidationUtils = Class.create();
AddressValidationUtils.prototype = {

	initialize: function() {
	},

	validateAddress: function(street, city, state, zip) {
		var response = {result: 'failure', street: street, city: city, state: state, zip: zip};

		var rest = new RESTMessage('US Street Address API', 'get');
		rest.setStringParameter('authid', gs.getProperty('us.address.service.auth.id'));
		rest.setStringParameter('authToken', gs.getProperty('us.address.service.auth.token'));
		rest.setStringParameter('street', encodeURIComponent(street));
		rest.setStringParameter('city', encodeURIComponent(city));
		rest.setStringParameter('state', encodeURIComponent(state));
		rest.setStringParameter('zip', encodeURIComponent(zip));
		var resp = rest.execute();
		var body = resp.getBody();
		if (resp.getStatusCode() == 200) {
			var respArray = [];
			try {
				respArray = JSON.parse(body);
			} catch (e) {
				gs.info('Exception: ' + e);
			}
			if (respArray && respArray.length > 0) {
				respObj = respArray[0];
				if (typeof respObj.analysis == 'object') {
					var validity = respObj.analysis.dpv_match_code;
					if (validity == 'Y' || validity == 'S' || validity == 'D') {
						response.result = 'valid';
						response.street = respObj.delivery_line_1;
						response.city = respObj.components.city_name;
						response.state = respObj.components.state_abbreviation;
						response.zip = respObj.components.zipcode;
						if (respObj.components.plus4_code) {
							response.zip += '-' + respObj.components.plus4_code;
						}
					} else {
						response.result = 'invalid';
					}
				} else {
					gs.info('Invalid Response Object: ' + JSON.stringify(respObj));
				}
			} else {
				gs.info('Invalid Response Body: ' + body);
			}
		} else {
			gs.info('Invalid HTTP Response Code: ' + resp.getStatusCode());
		}

		return response;
	},

	type: 'AddressValidationUtils'
};

Now all we need to do is test it out and make sure that it all works. Once again, we can use that same test case that was provided in their testing tool; we just have to pass the values as arguments to the function call. We can write a quick little stand-alone script for that that looks like this:

var avu = new AddressValidationUtils();
gs.info(JSON.stringify(avu.validateAddress('3901 SW 154th Ave', 'Davie', 'FL', '33331'), null, '\t'));

To actually run that, we can bring up the background script runner, paste it in, and click on the Run Script button.

Test script in the background script runner

… which gets us the following result:

{
	"result": "valid",
	"street": "3901 SW 154th Ave",
	"city": "Davie",
	"state": "FL",
	"zip": "33331-2613"
}

Not only did we validate the address, we also picked up the last four digits of the zip code, which was not a part of the original user input. And of course, we just proved that our new Script Include does, in fact, do what we intended for it to do. Chalk up another baby step along the path that we first laid out when we began this journey. So now what?

At this point, we actually have two different ways to go as far as our next step. Given that we have a working script to call when everything is working as it should, we could move on to the Client Script and see if we can now integrate this with the User Profile form, or we could turn our attention to those gs.info placeholders and start replacing those with calls to the function that logs Events. It all has to be done at some point, so it really doesn’t matter other than the fact that we have to choose. But, I guess we don’t have to choose right now. We can figure that out when it’s time to put together the next installment in the series.