Show:
var fs = require('fs');
var util = require('util');

var winston = require('winston');
var lodash = require('lodash');
var request = require('request');
var Promise = require('promise');

var DSN = require('./dsn').DSN;
var Projects = require('./projects');
var Organizations = require('./organizations');
var Releases = require('./releases');
var Teams = require('./teams');
var Events = require('./events');

/**
 * Client for the Sentry API.
 *
 * @example
 *   var Client = require('sentry-api').Client;
 *
 *   var sentry = new Client('https://abc123:@app.getsentry.com/1234', {
 *     logging: true
 *   });
 *
 *   sentry.projects.get('my-organization-slug', 'my-project-slug').then(function(error, project) {
 *     console.log(project.name);
 *   });
 *
 * @class Client
 * @constructor
 * @param {String}  dsn    Sentry DSN.
 * @param {Options} config Config
 * @param {Number}  config.version Sentry API version number.
 */
var Client = function(dsn, config) {
  // If no DSN is provided, use the default.
  if (lodash.isObject(dsn)) {
    config = dsn;
    dsn = 'https://app.getsentry.com';
  }

  this.dsn = new DSN(dsn);

  this.config = lodash.defaults(config || {}, {
    version: 0,
    logging: false,
    token: null,
  });

  this.logger = new(winston.Logger)({
    transports: this.config.logging ? [
      new (winston.transports.Console)({
        colorize: true,
        label: 'SentryAPI'
      })
    ] : []
  });

  this.projects = new Projects(this);
  this.organizations = new Organizations(this);
  this.releases = new Releases(this);
  this.teams = new Teams(this);
  this.events = new Events(this);
};

/**
 * Contains methods related to Sentry Projects.
 *
 * @property {Projects} projects
 */
Client.prototype.projects = null;

/**
 * Contains methods related to Sentry Organizations.
 *
 * @property {Organizations} organizations
 */
Client.prototype.organizations = null;

/**
 * Contains methods related to Sentry Releases.
 *
 * @property {Releases} releases
 */
Client.prototype.releases = null;

/**
 * Contains methods related to Sentry Teams.
 *
 * @property {Teams} teams
 */
Client.prototype.teams = null;

/**
 * Contains methods related to Sentry Events.
 *
 * @property {Events} events
 */
Client.prototype.events = null;

/**
 * Make a request to the Sentry API.
 *
 * @method request
 * @param  {String}   path     The request path.
 * @param  {Object}   options  Request options. These are the same as the request module's options.
 * @param  {Function} callback Function to execute when the request completes.
 * @return {Promise}
 */
Client.prototype.request = function(path, options, callback) {
  var uri = util.format('%s/api/%d/%s', this.dsn.uri, this.config.version, path);

  this.logger.info(options.method, uri);

  if (lodash.isUndefined(callback)) {
    callback = function() {};
  }

  return new Promise(function (resolve, reject) {
    var requestOptions = lodash.extend({
      uri: uri,
      json: true,
    }, options);

    if (this.config.token) {
      requestOptions.auth = {
        bearer: this.config.token,
      };
    }
    else {
      requestOptions.auth = {
        user: this.dsn.publicKey,
      };
    }

    request(requestOptions, function(error, response, body) {
      if (error) {
        this.logger.error(error);
        callback(error, null);
        reject(error);
      }
      else if (response.statusCode >= 200 && response.statusCode < 300) {
        this.logger.info(response.statusCode, response.statusMessage);
        callback(null, body);
        resolve(body);
      }
      else {
        this.logger.warn(response.statusCode, response.statusMessage);
        if (body && body.detail) {
          error = new Error(body.detail);
        }
        else if (response.statusMessage) {
          error = new Error(util.format('%d: %s', response.statusCode, response.statusMessage));
        }
        else {
          error = new Error(response.statusCode);
        }
        callback(error);
        reject(error)
      }
    }.bind(this));
  }.bind(this));
};

/**
 * Convenience method for making GET requests.
 *
 * @method get
 * @param  {String}   path     Request path.
 * @param  {Object}   params   Request query string parameters.
 * @param  {Function} callback Request callback.
 * @return {Promise}
 */
Client.prototype.get = function(path, params, callback) {
  return this.request(path, {method: 'GET', qs: params}, callback);
};

/**
 * Convenience method for making POST requests.
 *
 * @method post
 * @param  {String}   path     Request path.
 * @param  {Object}   body     Request body.
 * @param  {Function} callback Request callback.
 * @return {Promise}
 */
Client.prototype.post = function(path, body, callback) {
  return this.request(path, {method: 'POST', body: body}, callback);
};

/**
 * Convenience method for making DELETE requests.
 *
 * @method delete
 * @param  {String}   path     Request path.
 * @param  {Function} callback Request callback.
 * @return {Promise}
 */
Client.prototype.delete = function(path, callback) {
  return this.request(path, {method: 'DELETE'}, callback);
};

/**
 * Convenience method for making PUT requests.
 *
 * @method put
 * @param  {String}   path     Request path.
 * @param  {Object}   body     Request body.
 * @param  {Function} callback Request callback.
 * @return {Promise}
 */
Client.prototype.put = function(path, body, callback) {
  return this.request(path, {method: 'PUT', body: body}, callback);
};

exports.Client = Client;