API Docs for:
Show:

File: app/store/env/base.js

'use strict';

/**
 * The base store environment.
 *
 * @module env
 * @submodule env.base
 */

YUI.add('juju-env-base', function(Y) {

  /**
   * The Base Juju environment.
   *
   * This class is intended to be subclassed by real environment
   * implementations.
   *
   * @class BaseEnvironment
   */
  function BaseEnvironment(config) {
    // Invoke Base constructor, passing through arguments.
    BaseEnvironment.superclass.constructor.apply(this, arguments);
  }

  BaseEnvironment.NAME = 'base-env';
  BaseEnvironment.ATTRS = {
    /**
      The websocket URL to connect to.

      @attribute socket_url
      @type {string}
    */
    'socket_url': {},
    /**
      The connection object.

      @attribute conn
      @type {object}
    */
    'conn': {},
    /**
      The username used to connect.

      @attribute user
      @type {string}
    */
    'user': {},
    /**
      The password used to connect.

      @attribute password
      @type {string}
    */
    'password': {},
    /**
      Whether or not the connection is open.

      @attribute connected
      @type {boolean}
    */
    'connected': {value: false},
    /**
      Whether or not to run in debug mode.

      @attribute debug
      @type {boolean}
    */
    'debug': {value: false},
    /**
      Whether or not to run in read-only mode

      @attribute readOnly
      @type {boolean}
    */
    'readOnly': {value: false},
    /**
      The default series (e.g.: precise) as provided by juju.

      @attribute defaultSeries
      @type {string}
    */
    'defaultSeries': {},
    /**
      The provider type (e.g.: ec2) as provided by juju.

      @attribute providerType
      @type {string}
    */
    'providerType': {},
    /**
      The environment name as provided by juju.

      @attribute environmentName
      @type {string}
    */
    'environmentName': {}
  };

  Y.extend(BaseEnvironment, Y.Base, {

    initializer: function() {
      // Define custom events.
      this.publish('msg', {
        emitFacade: true,
        defaultFn: this.dispatch_result
      });
      // txn-id sequence.
      this._counter = 0;
      // mapping txn-id callback if any.
      this._txn_callbacks = {};
      // Consider the user unauthenticated until proven otherwise.
      this.userIsAuthenticated = false;
      this.failedAuthentication = false;
      // Populate our credentials if they don't already exist.
      var credentials = this.getCredentials() || {};
      if (Y.Lang.isValue(this.get('user'))) {
        credentials.user = credentials.user ||
            this.get('user');
        if (Y.Lang.isValue(this.get('password'))) {
          credentials.password = credentials.password ||
              this.get('password');
        }
      }
      this.setCredentials(credentials);
    },

    destructor: function() {
      // Close the socket, if we have connected.
      if (this.ws) {
        this.ws.close();
      }
      this._txn_callbacks = {};
    },

    connect: function() {
      // Allow an external websocket to be passed in.
      var conn = this.get('conn');
      if (conn) {
        this.ws = conn;
      } else {
        this.ws = new Y.ReconnectingWebSocket(this.get('socket_url'));
      }
      this.ws.debug = this.get('debug');
      this.ws.onmessage = Y.bind(this.on_message, this);
      this.ws.onopen = Y.bind(this.on_open, this);
      this.ws.onclose = Y.bind(this.on_close, this);
      // Our fake backends have "open" methods.  Call them, now that we have
      // set our listeners up.
      if (this.ws.open) {
        this.ws.open();
      }
      return this;
    },

    on_open: function(data) {
      this.set('connected', true);
    },

    on_close: function(data) {
      this.set('connected', false);
    },

    /**
     * Fire a "msg" event when a message is received from the WebSocket.
     *
     * @method on_message
     * @param {Object} evt The event triggered by the WebSocket.
     * @return {undefined} Fire an event only.
     */
    on_message: function(evt) {
      this.fire('msg', Y.JSON.parse(evt.data));
    },

    /**
     * Dispatch the results returned by the API backend.
     * Take care of calling attached callbacks and firing events.
     * Subclasses must implement the "_dispatch_rpc_result" and
     * "_dispatch_event" methods or override this method directly.
     *
     * @method dispatch_result
     * @param {Object} data The JSON contents returned by the API backend.
     * @return {undefined} Dispatches only.
     */
    dispatch_result: function(data) {
      this._dispatch_rpc_result(data);
      this._dispatch_event(data);
    },

    /**
     * Fire a "permissionDenied" event passing the attempted operation.
     *
     * @method _firePermissionDenied
     * @private
     * @param {Object} op The attempted operation (with an "op" attr).
     * @return {undefined} Fires an event only.
     */
    _firePermissionDenied: function(op) {
      var title = 'Permission denied';
      var message = ('GUI is in read-only mode and this operation ' +
          'requires an environment modification');
      console.warn(title + ': ' + message + '. Attempted operation: ', op);
      this.fire('permissionDenied', {title: title, message: message, op: op});
    },

    /**
     * Store the user's credentials in session storage.
     *
     * @method setCredentials
     * @param {Object} The credentials to store, with a 'user' and a 'password'
     *                 attribute included.
     * @return {undefined} Stores data only.
     */
    setCredentials: function(credentials) {
      sessionStorage.setItem('credentials', Y.JSON.stringify(credentials));
    },

    /**
     * Retrieve the stored user credentials.
     *
     * @method getCredentials
     * @return {Object} The stored user credentials with a 'user' and a
     *                   'password' attribute.
     */
    getCredentials: function() {
      var credentials = Y.JSON.parse(sessionStorage.getItem('credentials'));
      if (credentials) {
        Object.defineProperties(credentials, {
          areAvailable: {
            /**
             * Returns whether or not credentials are populated.
             *
             * @method get
             * @return {Boolean} Whether or not user and password are set.
             */
            get: function() {
              return Y.Lang.isValue(this.user) &&
                  Y.Lang.isValue(this.password);
            }
          }
        });
      }
      return credentials;
    },

    /**
     * Clear login information.
     *
     * @method logout
     * @return {undefined} Nothing.
     */
    logout: function() {
      this.userIsAuthenticated = false;
      this.setCredentials(null);
      this.ws.close();
      this.connect();
    }

  });

  Y.namespace('juju.environments').BaseEnvironment = BaseEnvironment;

}, '0.1.0', {
  requires: [
    'base',
    'json-parse',
    'json-stringify',
    'reconnecting-websocket'
  ]
});