Source: analytics/analytics.js

/**
 * Copyright© 2016, Oracle and/or its affiliates. All rights reserved.
 */

/**
 * Callback invoked after successfully flushing analytics events.
 * @callback Analytics~successCallback
 * @deprecated Use promises instead
 */

/**
 * Callback invoked on error.
 * @callback Analytics~errorCallback
 * @param statusCode {Number} Any HTTP status code returned from the server, if available.
 * @param message {String} The HTTP payload from the server, if available, or an error message.
 * @deprecated Use promises instead
 */


/**
 *
 * Class that provides analytics capabilities. Callers should use
 * MobileBackend's [analytics]{@link MobileBackend#analytics} property.
 * @constructor
 * @global
 */
function Analytics(backend, platform, utils, logger) {

    var _sessionId = null;
    var _events = [];

  /**
   * Returns session ID for current session.
   * @returns {String}
   */
  this.getSessionId = function(){
    return _sessionId;
  };

  this._getEvents = function(){
    return _events;
  };

  /**
   * Starts a new session. If one is in progress, then a new session will not be created.
   */
  this.startSession = function () {
    if (_sessionId === null) {
      if(locationEnabled()){
        platform.initGPSLocation();
      }
      _sessionId = utils.uuid();
      this.logNamedEvent('sessionStart').type = 'system';
    }
  };

    /**
     * Ends a session if one exists.
     * @param [successCallback] {Analytics~successCallback} Callback invoked on success (deprecated use promises instead).
     * @param [errorCallback] {Analytics~errorCallback} Callback invoked on error (deprecated use promises instead).
     * @return {Promise.<Undefined|NetworkResponse>}
     */
  this.endSession = function (successCallback, errorCallback) {
    if (_sessionId !== null) {
      var _this = this;
      _this.logNamedEvent("sessionEnd").type = "system";
      logger.verbose('Deactivate a default session');
      return this.flush().then(flushSuccess, flushError);
    } else {
      if(errorCallback){
        errorCallback(500, 'Session ID is null');
        return undefined;
      } else {
        return Promise.reject(new NetworkResponse(500, 'Session ID is null'));
      }
    }

    function flushSuccess(){
      _sessionId = null;
      if(successCallback){
        successCallback();
      }
    }

    function flushError(exception){
      if(errorCallback){
        errorCallback(exception.statusCode, exception.data);
      } else {
        return Promise.reject(exception);
      }
    }
  };

  /**
   * Creates a new analytics event with the given name.
   * @param name {String} The name of the event.
   * @returns {AnalyticsEvent} The [AnalyticsEvent]{@link AnalyticsEvent} instance that was logged.
   */
  this.logNamedEvent = function (name) {
    var event = new AnalyticsEvent(name);
    this.logEvent(event);
    return event;
  };

  /**
   * Writes out an analytics event. It will implicitly call startSession(),
   * which will add a new event to the list of events for Oracle Mobile Cloud Service to consume
   * @param event {AnalyticsEvent} The event to log.
   * @example event: "GettingStartedJSEvent"
   * @returns {AnalyticsEvent} The [AnalyticsEvent]{@link AnalyticsEvent} instance that was logged.
   */
  this.logEvent = function (event) {
    if (_events.length === 0) {
      _events[0] = this._createContextEvent();
    }

    this.startSession();
    _events[_events.length] = event;
    event.sessionID = _sessionId;

    return event;
  };

  function locationEnabled(){
    if(backend._config.analytics && typeof backend._config.analytics.location !== 'undefined'){
      return backend._config.analytics.location;
    } else {
      return true;
    }
  }

      /**
     * Uploads all events to the service if the device is online or caches them locally until the device goes online, at
     * which point they will be uploaded. If a session is in progress it will end.
     * @param [successCallback] {Analytics~successCallback} Callback invoked on success (deprecated use promises instead).
     * @param [errorCallback] {Analytics~errorCallback} Callback invoked on error (deprecated use promises instead).
     * @return {Promise.<Object|NetworkResponse>}
     */
  this.flush = function (successCallback, errorCallback) {
    for (var i = 0; i < _events.length; i++) {
      if (locationEnabled() && _events[i].name == "context") {

        var gpsLocation = platform.getGPSLocation();
        if (gpsLocation != null && gpsLocation.latitude != null) {
          _events[i].properties.latitude = gpsLocation.latitude;
        }

        if (gpsLocation != null && gpsLocation.longitude != null) {
          _events[i].properties.longitude = gpsLocation.longitude;
        }
      }
    }

    var eventsString = JSON.stringify(_events);

    var headers = backend.getHttpHeaders(utils.MODULE_NAMES.MCS_ANALYTICS);
    headers[utils.HEADERS.CONTENT_TYPE] = utils.ACCEPT_TYPES.APPLICATION_JSON;

    return platform.invokeService({
      method: utils.HTTP_METHODS.POST,
      url: backend.getPlatformUrl("analytics/events"),
      headers: headers,
      data: eventsString
    }).then(invokeServiceSuccess, invokeServiceError);

    function invokeServiceSuccess() {
      logger.verbose('Analytics events flushed.');
      _events = [];
      if (successCallback) {
        successCallback();
      }
    }

    function invokeServiceError(response) {
      logger.error('Failed to flush analytics events.');
      if (errorCallback) {
        errorCallback(response.statusCode, response.data);
      } else {
        return Promise.reject(response);
      }
    }
  };

  this._createContextEvent = function () {
    var contextEvent = new AnalyticsEvent("context");
    contextEvent.type = "system";
    contextEvent.properties.timezone = "" + new Date().getTimezoneOffset() * 60;

    var deviceInformation = platform.getDeviceInformation();
    contextEvent.properties.model = deviceInformation.model;
    contextEvent.properties.manufacturer = deviceInformation.manufacturer;
    contextEvent.properties.osName = deviceInformation.osName;
    contextEvent.properties.osVersion = deviceInformation.osVersion;
    contextEvent.properties.osBuild = deviceInformation.osBuild;
    contextEvent.properties.carrier = deviceInformation.carrier;

    return contextEvent;
  };
}