2

I've been working on an audio player for the Amazon Echo. Amazon provided a good example to work off of (https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs). I'm coming from Python and node.js is still new to me, so this has been a fun exercise.

I have multiple streams that I'd like to play based on the user's timezone. As the Amazon Echo doesn't have an easy way to do this, I've decided to prompt the user for their zipcode and use that to determine their timezone.

Initial attempt used a JSON file of all relevant zipcode/timezones pairs, but it took too long to load.

Attempts to load them into a database met with mixed results, as I was having trouble configuring the Alexa skill to use multiple DBs. (I'm using one to track user sessions.)

Eventually, I settled on using a third-party API to determine the location (https://www.zipcodeapi.com/API#zipToLoc). Testing in the node shell went swimmingly, so I uploaded it to my lambda to test.

Unfortunately, tests have been giving me "Cannot read property 'body' of undefined".

Offending Code

    var constants = require(constants);
    var request = require(request);

    var zipcode = 85223; // Normally, this is taken from the intent.
    var request_string = 'https://www.zipcodeapi.com/rest/' + constants.zipAPIKey + '/info.json/' + zipcode + '/degrees';
    var bd = request(request_string, function(error, response, body){ 
                                    return body; 
                            });
    var proc_body = JSON.parse(bd.response.body); // THE PROBLEM LINE

    if (proc_body.hasOwnProperty('error_code')){
            var message = 'An error occurred. Try again later.';
            console.log("ZipCodeAPI Error : %j", proc_body.error_msg);
            this.attributes['utc'] = -5; // Default to EST
    } else {
            this.attributes['utc'] = proc_body.timezone.utc_offset_sec / 3600; // utc_offset_sec is in seconds. Bump it back to hours.
            var message = 'Your system has been configured.';
    }

Courtesy of the shell: JSON.parse(bd.response.body)

   { 
     zip_code: '85223',
     lat: 32.740013,
     lng: -111.679788,
     city: 'Arizona City',
     state: 'AZ',
     timezone:
      { timezone_identifier: 'America/Phoenix',
        timezone_abbr: 'MST',
        utc_offset_sec: -25200,
        is_dst: 'F' },
     acceptable_city_names: [] 
   }

As I said before, it works fine while in the shell. I'm not sure why it's giving me issues in the lambda. Is the request asynchronous, and the Echo isn't waiting to hear back from the API? Did I not configure something correctly?

I appreciate any help you're able to provide. Thank you for your time.

EDIT: As per the comment below, I reread the documentation. I'm guessing it's because I was reaching beyond the scope?

 'GetZip' : function () {
      this.attributes['zipcode'] = this.event.request.intent.slots.zipcode.value;
      var request_string = 'https://www.zipcodeapi.com/rest/' + constants.zipKey + '/info.json/' + this.attributes['zipcode'] + '/degrees';
      request(request_string, function(error, response, body){
           var proc_body = JSON.parse(body);
           if (proc_body.hasOwnProperty('error_code')){
                var message = 'An error occurred. Try again later.';
                console.log("ZipCodeAPI Error : %j", proc_body.error_msg);
                this.attributes['utc'] = -5;
           } else {
                this.attributes['utc'] = proc_body.timezone.utc_offset_sec / 3600;
                var message = 'Your system has been configured.';
           }
           this.handler.state = constants.states.START_MODE;
           this.response.speak(message);
           controller.play.call(this);
      });

I think there's still a scope issue, since this can't be accessed.

Mike C.
  • 21
  • 2
  • 1
    clearly `bd` doesn't have a `response` property (and probably shouldn't.) I suspect you aren't following the documentation for your `request` method. – Kevin B Nov 11 '16 at 22:01
  • I looked over the documentation as you requested. It appears that it was a scope issue. Now, I'm just having an issue accessing `this` in the callback function. Would using `bind()` on the callback function be a good way to resolve this? – Mike C. Nov 14 '16 at 15:53
  • It would be one way of solving it, yes. A lambda would fix it too, as would defining a 'self = this' var – Kevin B Nov 14 '16 at 16:00
  • Thanks for the quick response! I've been trying to bind it as `(function(t) { request(...);})(this)` to no effect. Figure I'll try the other means. Could you elaborate on the `self=this` var? – Mike C. Nov 14 '16 at 16:39
  • exactly that, yes. I prefer using an arrow function, but if you aren't transpiling your code from ES6 to ES5 then it may be better to use `.bind` or `var that = this` for compatability purposes. – Kevin B Nov 14 '16 at 16:40
  • Oh, this is node, then if you're using a more recent version use the arrow function. No transpiling needed. – Kevin B Nov 14 '16 at 16:42
  • Yeah, I'm still getting used to node. I'm coming from Python, so all this is new to me. The best I'm getting regarding arrow functions is [this](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions). Worth looking into? – Mike C. Nov 14 '16 at 16:50
  • Yes, absolutely. That's the best location for information on it. I'd suggest also reading up on let/const – Kevin B Nov 14 '16 at 16:51
  • Thanks. Arrow function is doing some work. Still have issues, but they fall outside of the scope of this question. – Mike C. Nov 14 '16 at 21:34

0 Answers0