Source: org/terraswarm/accessor/accessors/web/contextAware/ContextAware.js

// Copyright (c) 2015-2016 The Regents of the University of California.
// All rights reserved.
//
// Permission is hereby granted, without written agreement and without
// license or royalty fees, to use, copy, modify, and distribute this
// software and its documentation for any purpose, provided that the above
// copyright notice and the following two paragraphs appear in all copies
// of this software.
//
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
// ENHANCEMENTS, OR MODIFICATIONS.
//

/**
 * This accessor discovers REST services on the web and and generates a concrete
 * REST accessor for the service. It requires the contextAware module and
 * interface definition of concrete REST services. Please see:
 * https://www.icyphy.org/accessors/wiki/Version0/ContextAware
 *
 * Example usage of ContextAware accessor:
 *
 * This accessor generalizes the capability of REST.js by providing a simple
 * interface for a user to select a concrete REST service and provides the
 * context (set of inputs needed) to invoke that concrete REST service and set
 * of outputs needed from the service.
 *
 * Currently it is coded for three concrete REST services (GSN, Firebase, and
 * Paraimpu), but can be expanded to any number of REST services as long as the
 * interface of the service can be defined. The three services that this
 * accessor works with are the GSN, which is a global sensor network server
 * hosted in Texas State university, the Paraimpu which is a social aware
 * middleware for Internet of Things and Firebase which is a cloud service from
 * Google that provides storage for IoT.
 *
 * This is just an experimental system, so the UI is very primitive and basic.
 * To experiment with it, do the following:
 *
 * <ul>
 * <li> double click on the accessor, make a selection, press commit,</li>
 * <li> double click on the accessor, press the reload button and then press
 * commit,</li>
 * <li> double click again and the required input for the selected REST service
 * will appear on the editor menu</li>
 * <li> for GSN, enter 'pluto.cs.txstate.edu' for the host, enter '22001' for
 * the port, select the specific output dataType or leave it as "all". Leave the
 * rest as default</li>
 * <li> for Paraimpu, enter 'api.paraimpu.com' for the host, enter '443' for the
 * port, enter this <code>46e0ee55195c4dd9dca295a7ac8282d28f4a2259</code> for
 * access token. Select the dataType for output or leave it as 'all'</li>
 * <li> for Firebase, enter 'sizzling-fire-8605.firebaseio.com' for the host,
 * enter '443' for port, leave the rest as default. Select the appropriate
 * dataType to output or leave it as 'all'. </li>
 * </ul>
 *
 * @accessor contextAware/contextAware
 * @author Anne H. Ngu (angu@txstate.edu)
 * @input {number} input to the accessor
 * @parameter{string} the name of the REST service that context aware tries to
 *                    adapt. A list of available services are presented as
 *                    option. This is obtained by the function service() which
 *                    is defined in the supporting module
 *
 * @version $$Id$$
 */

// 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 */
/*global addInputHandler, console, exports, extend, get, getParameter, implement, input, output, parameter, require, send */
/*jshint globalstrict: true*/
"use strict";

var contextAware = require("contextAware");

// Initialize the context aware service discovery class. Not used currently.
var contextAwareService = new contextAware.DiscoveryOfRESTService();

// The service that was selected by the user.
var selectedService;

/**
 * Define the input and the choice of REST services to which this accessor may
 * adapt.
 */
exports.setup = function () {
    this.input('input');
    // a simple UI interface to start the dialog with users to select a REST
    // service
    this.parameter('RESTSource', {
        'type': 'string',
        'value': 'Make a selection',
        'options': contextAware.services()
    });
    selectedService = this.getParameter('RESTSource');
    // implement the selected service's input and output ports
    if (selectedService == 'GSN') {
        this.implement("contextAware/GSNInterface.js");
        this.input('dataType', {
            'type': 'string',
            'value': 'all',
            'options': contextAware.gsnServices()
        });
    } else if (selectedService == 'Paraimpu') {
        this.implement("contextAware/ParaimpuInterface.js");
        this.input('dataType', {
            'type': 'string',
            'value': 'all',
            'options': contextAware.paraimpuServices()
        });
    } else if (selectedService == 'Firebase') {
        this.implement("contextAware/FirebaseInterface.js");
        this.input('dataType', {
            'type': 'string',
            'value': 'all',
            'options': contextAware.firebaseServices()
        });
    } else {
        console.log("REST Service interface not available");
    }
    this.extend("net/REST.js");
    // hide the input and output ports of the inherited accessor
    this.input('command', {
        'visibility': 'expert'
    });
    this.input('arguments', {
        'visibility': 'expert'
    });
    this.input('options', {
        'visibility': 'expert'
    });
    this.output('headers', {
        'visibility': 'expert'
    });
    this.input('body', {
        'visibility': 'expert'
    });
    this.input('trigger', {
        'visibility': 'expert'
    });
};

/**
 * Upon receiving details of a REST service, construct a concrete REST accessor
 * to access the service.
 */
exports.initialize = function () {
    // The superclass registers a handler for the 'trigger' input
    // to issue an HTTP request based on the current inputs.
    this.ssuper.initialize();

    var self = this;

    // Add a handler for the 'input' input.
    this.addInputHandler(
        'input',
        function () {
            // construct the URL for the selected service
            var serviceURL = {
                "url": {
                    "host": self.getParameter('host'),
                    "port": self.getParameter('port'),
                    "protocol": self.getParameter('protocol')
                }
            };
            self.send('options', serviceURL);
            self.send('command', self.getParameter('path'));
            if (selectedService == 'Paraimpu') {
                // sample access token to use
                // "46e0ee55195c4dd9dca295a7ac8282d28f4a2259"
                var arg = {
                    "access_token": self.getParameter('accessToken')
                };
                console.log("org/terraswarm/accessor/accessors/web/contextAware/ContextAware.js: access_token:" +
                    arg);
                self.send('arguments', arg);
            }

            // ex. of valid json format for reference
            // self.send('options', {"url":"http://pluto.cs.txstate.edu:22001"});
            // self.send('options',
            // {"url":{"host":"pluto.cs.txstate.edu","port":22001}});

            // Cause the base class handler to issue the HTTP request.
            self.send('trigger', true);
        });
};

/**
 * Filter the response from Firebase. Extracting data about microwave, its last
 * reading and current status
 */
var getFirebaseData = function (response) {
    var type = this.get('dataType');
    var result = JSON.parse(response);
    switch (type) {
    case "microwave":
        this.send('microwave', result.Microwave);
        // console.log("ContextAwareTest filterResponse() " +
        // JSON.stringify(result.Microwave));
        break;
    case "microwaveStatus":
        this.send('microwaveStatus', result.Microwave.status);
        break;
    case "pastValues":
        this.send('pastValues', result.Microwave.pastValues);
        break;
    case "all":
        this.send('microwave', result.Microwave);
        this.send('microwaveStatus', result.Microwave.status);
        this.send('pastValues', result.Microwave.pastValues);
        break;
    default:
        this.send('microwave', result.Microwave);
    }
};

/*
 * Filter response from Paraimpu. Extracting the wind speed and the sensor that
 * produces the wind speed
 *
 */
var getParaimpuData = function (response) {
    var type = this.get('dataType');
    var result = JSON.parse(response);
    switch (type) {
    case "payload":
        this.send('payload', result.payload);
        // console.log("ContextAwareTest filterResponse() " +
        // JSON.stringify(result.payload));
        break;
    case "sensorId":
        this.send('sensorId', result.thingId);
        break;
    case "producer":
        this.send('producer', result.producer);
        break;
    case "all":
        this.send('payload', result.payload);
        this.send('sensorId', result.thingId);
        this.send('producer', result.producer);
        break;
    default:
        this.send('response', result);
    }
};

/**
 * Filter response from GSN. The response is in xml format which needs to be
 * converted first to json. Then extract data about the Phidget sound sensor. A
 * more generic way to extract the sensor data is needed in the future so that
 * there is no need to change this extraction logic when a different sensor data
 * is needed from this source
 *
 */
var getGSNData = function (response) {
    var type = this.get('dataType');
    var xmlJson = {};
    xmlJson = contextAware.xmlToJson(response);
    var result = JSON.parse(xmlJson);
    switch (type) {
    case "sound":
        // jsdoc was failing with "line 271: missing name after . operator"
        // This code has no tests because the GSN source on the web does not stay up.
        // http://stackoverflow.com/questions/19217365/missing-name-after-operator-yui-compressor-for-socket-io-js-files
        // suggests using ['..']
        // this.send('sound', result."virtual-sensor"[2].field[2]);
        this.send('sound', result['virtual-sensor'][2].field[2]);
        break;
    case "sensorName":
        // this.send('sensorName', result."virtual-sensor"[2].name);
        this.send('sensorName', result['virtual-sensor'][2].name);
        break;
    case "all":
        //send('sound', result."virtual-sensor"[2].field[2]);
        this.send('sound', result['virtual-sensor'][2].field[2]);
        //send('sensorName', result."virtual-sensor"[2].name);
        this.send('sensorName', result['virtual-sensor'][2].name);
        break;
    default:
        //send('response', result."virtual-sensor");
        this.send('response', result['virtual-sensor']);
    }
};

/**
 * Filter the response. It overrides the filterResponse() in the base class to
 * extract a portion of the response that is defined in the corresponding
 * service interface
 */
exports.filterResponse = function (response) {

    switch (selectedService) {
    case "GSN":
        getGSNData.call(this, response);
        break;
    case "Paraimpu":
        getParaimpuData.call(this, response);
        break;
    case "Firebase":
        getFirebaseData.call(this, response);
        break;
    }
    return response;
};