Home Reference Source Repository

src/Client.js

'use strict';

import Factory from './Factory';

var singleton = Symbol();
var singletonEnforcer = Symbol();

/**
 * Client is the responsible of comunicate with the API.
 */
class Client {
    
    /**
     * Constructor
     * 
     * @param {Symbol} enforcer 
     */
    constructor(enforcer) {
        this._token = null;
        this._clientId = null;
        this._secretId = null;
        this._scopes = null;
        this._redirect_uri = null;
        
        if (enforcer != singletonEnforcer) {
            throw "Cannot construct singleton";   
        }
    }
    
    /**
     * Get the current instance of Client
     * 
     * @return {Object} instance
     */
    static get instance() {
        if(!this[singleton]) {
          this[singleton] = new Client(singletonEnforcer);
        }
        return this[singleton];
    }

    /**
     * Set settings
     * 
     * @param {Object} settings Settings
     */
    set settings(settings) {
        this._token = settings.token;
        this._clientId = settings.clientId;
        this._secretId = settings.secretId;
        this._scopes = settings.scopes;
        this._redirect_uri = settings.redirect_uri;
    }

    /**
     * Set token
     * 
     * @param {String} data Token
     */
    set token(data) {
        this._token = data;
    }

    /**
     * Get the current token
     * 
     * @return {String} Current token
     */
    get token() {
        return this._token;
    }
    
    /**
     * This method fires a new window that allow login
     * 
     * @param  {Function} callback
     * @return {Function|Promise}
     */
    login(callback) {
        let url_login = 'https://accounts.spotify.com/en/authorize?response_type=token&client_id='+
                this._clientId+'&redirect_uri='+encodeURIComponent(this._redirect_uri)+
                ( this._scopes ? '&scope=' + encodeURIComponent( this._scopes) : '');
        if (callback) {
            return callback(url_login);
        } else {
            return new Promise((resolve) => {
                resolve(url_login);
            });
        }
    }

    /**
     * @param  {String} url Endpoint
     * @param  {String} method Method GET, POST, PUT or DELETE
     * @param  {Object} body Objet to send in the body
     * @return {Promise}
     */
    request(url, method, body) {
        return this.fetch(url, method, body).then(function(data) {
            return Factory(data);
        }.bind(this));
    }

    /**
     * Function that encode objects
     * 
     * @param  {Object} obj Object to encode
     * @return {String} A encode object
     */
    toQueryString(obj) {
      var str = [];
      for(var p in obj)
        if (obj.hasOwnProperty(p)) {
          str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
        }
      return str.join("&");
    }

    /**
     * @param  {String} endpoint Endpoint
     * @param  {String} method Method GET, POST, PUT or DELETE
     * @param  {Object} body Objet to send in the body
     * @return {Promise}
     */
    fetch(endpoint, method, body) {
        let _headers = { 'Accept': 'application/json'};
        let _url;
        let _body;

        if (this._token) {
            _headers.Authorization = `Bearer ${this._token}`;
        }

        if (endpoint.indexOf('https') > -1) {
            _url = endpoint;
        } else {
            _url = `https://api.spotify.com/v1${endpoint}`;
        }

        if (method === 'GET') {
            if (body) {
                let separator = _url.indexOf('?') !== -1 ? "&" : "?";
                _url = _url+separator+this.toQueryString(body);
            }
        } else {
            _body = JSON.stringify(body);
        }

        let checkStatus = (response) => {
            if (response.status >= 200 && response.status < 300) {
                return response;
            } else {
                var error = new Error(response.statusText);
                error.response = response;
                throw error;
            }
        }

        let parseJSON = (response) => {
            return response.json();
        }

        return fetch(_url, {
            method: method || 'GET',
            headers: _headers,
            body: _body
        }).then(checkStatus)
        .then(parseJSON)
    };
}

/**
 * Exports the Client class.
 */
export default Client;