API Docs for: 2.0.0
Show:

File: lib/client.js

/**
 * Upwork auth library for using with public API by OAuth2
 *
 * @package     UpworkAPI
 * @since       02/10/2018
 * @copyright   Copyright 2018(c) Upwork.com
 * @author      Maksym Novozhylov <mnovozhilov@upwork.com>
 * @license     Upwork's API Terms of Use {@link https://developers.upwork.com/api-tos.html}
 */

/**
 * @module client
 * @requires simple-oauth2
 */

debug('export client module');

const request = require('request');
const { ClientCredentials, AuthorizationCode } = require('simple-oauth2');

const UpworkLibraryUserAgent = 'Github Upwork API NodeJS Client';

/**
 * @class Client
 * @constructor
 */
function Client(config) {
  debug('starting client')
  debug(config.clientId, 'with client id');
  debug(config.clientSecret, 'with client secret');
  debug(config.state, 'with state');

  this.config = config;
  this.baseUrl = config.baseUrl;
  this.entryPoint = undefined;
  this.tenantId = undefined;
  
  var oauth2Config = {
    client: {
      id: config.clientId,
      secret: config.clientSecret
    },
    auth: {
      tokenHost: config.tokenHost,
      tokenPath: config.tokenPath,
    },
    http: {
      headers: {
        'User-Agent': UpworkLibraryUserAgent
      },
    }
  };

  if (this.config.grantType === 'client_credentials') {
    this.simpleOauth2Client = new ClientCredentials(oauth2Config);
  } else {
    oauth2Config.auth['authorizePath'] = config.authorizePath;
    this.simpleOauth2Client = new AuthorizationCode(oauth2Config);
  }

  debug(this.simpleOauth2Client, 'got an oauth2 client');
}

/**
 * Get authorization URL and request token
 *
 * @method getAuthorizationUrl
 */
Client.prototype.getAuthorizationUrl = function() {
  debug(this.config.redirectUri, 'get authorization url with the specific request uri');
  
  return this.simpleOauth2Client.authorizeURL({
    redirect_uri: this.config.redirectUri,
    state: this.config.state 
  });
}

/**
 * Get access/refresh tokens pair
 *
 * @method getToken
 * @param authzCode {String} Authorization code
 * @param callback
 */
Client.prototype.getToken = async function(authzCode, callback) {
  var accessToken;
  var error;

  try {
    if (this.config.grantType == 'client_credentials') {
      accessToken = await this.simpleOauth2Client.getToken();
    } else {
      accessToken = await this.simpleOauth2Client.getToken({
        code: authzCode,
        redirect_uri: this.config.redirectUri
      });
    }
    debug(accessToken, 'Received an access token');
  } catch (error) {
    debug(error.message, 'Can not get valid Access Token');
    debug(error, 'Error');
  }

  callback(error, accessToken);
}

/**
 * Set new access/refresh token pair
 *
 * @method setAccessToken
 * @param tokenPair
 * @param callback
 * @async
 */
Client.prototype.setNewAccessTokenPair = function (tokenPair, callback) {
  debug(tokenPair, 'setting access token from token pair');
  let aToken = this.simpleOauth2Client.createToken(tokenPair.token);

  this.config.accessToken = aToken.token.access_token;
  this.config.refreshToken = aToken.token.refresh_token;
  callback(aToken.token);
}

/**
 * Set known access/refresh token pair
 * 
 * @method setAccessToken
 * @param callback
 * @async
 */
Client.prototype.setAccessToken = function(callback) {
  debug('setting access token from config');
  let aToken = this.simpleOauth2Client.createToken({
      access_token: this.config.accessToken,
      refresh_token: this.config.refreshToken,
      expires_in: this.config.expiresIn,
      expires_at: this.config.expiresAt
  });

  if (aToken.expired()) {
    debug('the access token has expired, refreshing');
    aToken.refresh((error, result) => {
      aToken = result;
      debug(aToken, 'received a new AccessToken object');
      this.config.accessToken = aToken.token.access_token;
      callback(aToken.token);
    });
  } else {
    callback(aToken.token);
  }
}

/**
 * Specify entry point used in base url
 *
 * @method setEntryPoint
 * @param entryPoint {String} Entry point, e.g. 'api' or 'gds'
 */
Client.prototype.setEntryPoint = function(entryPoint) {
  debug('updating entry point');
  this.entryPoint = entryPoint;
  // a trick to support different entry points in node-upwork
  if (entryPoint === 'graphql') {
    this.baseUrl = this.config.gqlUrl;
  } else {
    this.baseUrl = this.baseUrl.replace(/\/api\//i, '/' + entryPoint + '/');
  }
}

/**
 * Configure X-Upwork-API-TenantId header
 *
 * @method setOrgUidHeader
 * @param tenantId {String} Organization UID
 * @param callback
 * @async
 */
Client.prototype.setOrgUidHeader = function(tenantId, callback) {
  debug('setting organization uid header');
  this.tenantId = tenantId;
}

/**
 * Send GET request
 *
 * @method get
 * @param url {String} Relative URL
 * @param params {Hash} Parameters
 * @param callback
 * @async
 */
Client.prototype.get = function(url, params, callback) {
  debug('running GET request');
  this.sendRequest('GET', url, params, callback);
}

/**
 * Send POST request
 *
 * @method post
 * @param url {String} Relative URL
 * @param params {Hash} Parameters
 * @param callback
 * @async
 */
Client.prototype.post = function(url, params, callback) {
  debug('running POST request');
  this.sendRequest('POST', url, params, callback);
}

/**
 * Send PUT request
 *
 * @method get
 * @param url {String} Relative URL
 * @param params {Hash} Parameters
 * @param callback
 * @async
 */
Client.prototype.put = function(url, params, callback) {
  debug('running PUT request');
  this.sendRequest('PUT', url, params, callback);
}

/**
 * Send DELETE request
 *
 * @method delete
 * @param url {String} Relative URL
 * @param params {Hash} Parameters
 * @param callback
 * @async
 */
Client.prototype.delete = function(url, params, callback) {
  debug('running DELETE request');
  this.sendRequest('DELETE', url, params, callback);
}

/**
 * Send request
 *
 * @method sendRequest
 * @param method {String} HTTP method
 * @param url {String} Relative URL
 * @param params {Hash} Parameters
 * @param callback
 * @async
 */
Client.prototype.sendRequest = function (method, url, params, callback) {
  debug('sending request');

  var _params;
  switch (method) {
    case 'GET':
      _params = {qs: params};
      break;
    case 'POST':
      if (this.entryPoint !== 'graphql') {
        _params = {formData: params};
      }
      break;
    default:
      _params = {formData: Object.assign(params, {http_method: method.toLowerCase()})};
      method = 'POST';
  }

  var _options = {
    method: method,
    url: this.baseUrl + url + ((this.entryPoint === undefined || this.entryPoint == 'api') ? '.json' : ''),
    auth: {
      bearer: this.config.accessToken
    },
    headers: {
      'User-Agent': UpworkLibraryUserAgent
    }
  };
  // GraphQL support
  if (this.entryPoint === 'graphql') {
    if (this.tenantId !== undefined) {
      _options.headers['X-Upwork-API-TenantId'] = this.tenantId;
    }
    _options.headers['Content-Type'] = 'application/json';
    _options.body = JSON.stringify(params);
  }

  debug(_options, 'Options for request');
  debug(_params, 'Params for request');

  request(Object.assign(_options, _params), (err, httpResponse, body) => {
    if (err) {
      debug(err, 'Request error');
      debug(httpResponse, 'httpResponse');
      debug(body, 'Body');
    }
    callback(err, httpResponse.statusCode, parseResponse(body));
  });
}

/**
 * Parse response body
 *
 * @method parseResponse
 * @param body {String} Response body
 */
function parseResponse(body) {
  try {
    return JSON.parse(body);
  } catch (e) {
    return {};
  }
}

module.exports = Client;