Source: org/terraswarm/accessor/accessors/web/services/YelpSearch.js

/** Perform a Yelp search for local buisnesses using the Yelp API
 *  https://www.yelp.com/developers/documentation/v3/business_search
 * 
 *  The full Yelp API allows many parameters for searching for buisness.
 *  This accessor demonstrates a simple query for a particular type of
 *  buisness near specific geographic coordinates.
 *
 *  This accessor requires an authentication token provided by Yelp for using
 *  their API. See https://www.yelp.com/developers/documentation/v3/authentication.
 *  This key must be stored in a text file at $KEYSTORE/yelp.txt
 *
 *  
 *  @accessor services/YelpSearch
 *  @author Matt Weber
 *  @version $$Id: YelpSearch.js 1725 2017-05-19 22:59:11Z cxh $$
 *  @input {string} searchTerm The variety of local buisness to search for (eg. "Restaurants").
 *  @input {number} latitude First part of coordinates for local buisness search. Defaults to UC Berkeley's coordinates.
 *  @input {number} longitude Second part of coordinates for local buisness search. Defaults to UC Berkeley's coordinates.
 *  @input trigger An input to trigger the search.
 *  @output {boolean} ready WARNING: Triggers may not work correctly before this output has been produced.
 *   This accessor produces true on this output when it has successfully loaded its API key
 *   and is ready for queries.
 *   If this accessor is acquring its API key asynchronously, this may occur after initialization has completed.
 *  @parameter {boolan} getAPIKeySynchronously Not all hosts support synchronous or asynchronous file reading.
 *   specify the right mode for this accessor's host here.
 *  @parameter {int} timeout The amount of time (in milliseconds) to wait for a response
 *   before triggering a null response and an error. This defaults to 5000.
 *  @output response Yelp's raw JSON response to the query.
 */


// Stop extra messages from jslint and jshint.  Note that there should
// be no space between the / and the * and global. See
// https://chess.eecs.berkeley.edu/ptexternal/wiki/Main/JSHint */
/*globals addInputHandler, addInputParameter, console, error, exports, extend, input, get, getParameter, getResource, output, parameter, send */
/*jshint globalstrict: true*/
'use strict';
 
 /** Set up the accessor by defining the inputs and outputs.
 */

exports.setup = function () {
    this.extend('net/REST');
    this.input('searchTerm', {
        'type': 'string',
        'value': ''
    });

    this.input('latitude', {
        'type': 'number',
        'value': 37.869060
    });

    this.input('longitude', {
        'type': 'number',
        'value': -122.270460
    });

    this.output('ready',{
        'type': 'boolean',
        'spontaneous': true
    });

    this.parameter('getAPIKeySynchronously', {
        'type': 'boolean',
        'value': true
    });

    //Use the response output from the REST accessor

    // Change default values of the base class inputs.
    // Also, hide base class inputs, except trigger.
    this.input('options', {
        'visibility': 'expert',
    });
    this.input('command', {
        'visibility': 'expert',
    });
    this.input('arguments', {
        'visibility': 'expert',
    });
    this.input('body', {
        'visibility': 'expert'
    });
    this.output('headers', {
        'visibility': 'expert'
    });
    this.output('status', {
        'visibility': 'expert'
    });
    this.parameter('outputCompleteResponseOnly', {
        'visibility': 'expert'
    });
};

//Overriding REST
// exports.filterResponse = function(response){
//     return JSON.parse(response);
// };

//Overriding REST
//Connections to the Yelp server should be closed once data has been received.
exports.handleResponse = function(message){
     exports.ssuper.handleResponse.call(this, message);
     exports.ssuper.stopPendingRequest.call(this);
};


//Prepare accessor for a query with default input values
var options = {
    'method'  : 'GET',
    'url'     : "https://api.yelp.com"
};
var command = "/v3/businesses/search";


exports.initialize = function(){
    exports.ssuper.initialize.call(this);
    var thiz = this;
    var getResourceOptions = 1000; //1 second timeout on getResource

    var key = '';
    // The key from https://www.yelp.com/developers/documentation/v3/authentication 
    // that for Node and Nashorn hosts should be placed in $HOME/.ptKeystore/weatherKey.
    // For the Cordova host this should be src/www/keystore/yelp.txt
    // See the accessor comment for how to get the key.
    var keyFile = '$KEYSTORE/yelp.txt';

    if(thiz.getParameter('getAPIKeySynchronously')){
        try {
            key = getResource(keyFile, getResourceOptions).trim();
        } catch (e) {
            console.log('YelpSearch.js: Could not get ' + keyFile + ":  " + e +
                        '\nThe key is not public, so this accessor is only useful ' +
                        'If you have the key.  See ' +
                        'https://ptolemy.berkeley.edu/accessors/library/index.html?accessor=services.YelpSearch');
            key = 'ThisIsNotAPipeNorIsItAWorkingKeySeeTheYelpSearchAccessorDocs';
        }

        var authString = "Bearer " + key;
        options.headers = {"Authorization": authString};        

        thiz.send('ready', true);
    } else {
        var keyFile = '$KEYSTORE/yelp.txt';
        getResource(keyFile, getResourceOptions, function(status, resource){
            if(status != null){
                console.log("Error getting yelp API Key in YelpSearch: " + status);
            } else {
                if(resource == null){
                    console.log("Error in YelpSearch: resourceContents is null but status is null");
                } else {
                    key = resource.trim();

                    var authString = "Bearer " + key;
                    options.headers = {"Authorization": authString};        

                    thiz.send('ready', true);
                }
            }   
        });
    }
};

//Override
exports.issueCommand = function (callback){
    var thiz = this;
    var args = {'latitude'  : this.get('latitude').toString(),
                'longitude' : this.get('longitude').toString(),
                'term'      : this.get('searchTerm')
                };

    //Note, send('arguments', args) doesn't work because
    //send makes an input available in the _next_ reaction
    this.provideInput('options', options);
    this.provideInput('command', command);
    this.provideInput('arguments', args);
    exports.ssuper.issueCommand.call(this, callback);
}