Source: API/RestClient.js

/*
 * Copyright 2017-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
 * the License. A copy of the License is located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
 * and limitations under the License.
 */

import Signer from '../Common/Signer';
import { ConsoleLogger as Logger } from '../Common';

import Auth from '../Auth';
import * as AWS from 'aws-sdk';
import axios from 'axios';
const logger = new Logger('RestClient');

/**
* HTTP Client for REST requests. Send and receive JSON data.
* Sign request with AWS credentials if available
* Usage:
<pre>
const restClient = new RestClient();
restClient.get('...')
    .then(function(data) {
        console.log(data);
    })
    .catch(err => console.log(err));
</pre>
*/
export class RestClient {
    _options;

    /**
    * @param {RestClientOptions} [options] - Instance options
    */
    constructor(options) {
        const { endpoints } = options;
        this._options = options;
        logger.debug('API Options', this._options);
    }

    /**
    * Update AWS credentials
    * @param {AWSCredentials} credentials - AWS credentials
    *
    updateCredentials(credentials: AWSCredentials) {
        this.options.credentials = credentials;
    }
*/
    /**
    * Basic HTTP request. Customizable
    * @param {string} url - Full request URL
    * @param {string} method - Request HTTP method
    * @param {json} [init] - Request extra params
    * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
    */
    async ajax(url, method, init) {
        logger.debug(method + ' ' + url);

        var parsed_url = this._parseUrl(url);

        var params = {
            method: method,
            url: url,
            host: parsed_url.host,
            path: parsed_url.path,
            headers: {},
            data: null
        };

        let libraryHeaders = {}

        if (!init) {
            init = {}
        }

        if (init.body) {
            libraryHeaders['content-type'] = 'application/json';
            params.data = JSON.stringify(init.body);
        }

        //params.headers = { ...libraryHeaders, ...init.headers }

        if (init.headers) {
            Object.keys(init.headers).map(key => {
                params.headers[key] = init.headers[key];
            })
        }

        Object.keys(libraryHeaders).map(key => {
            params.headers[key] = libraryHeaders[key];
        })

        const credPromise = new Promise((resolve, reject) => {
            Auth.currentCredentials()
                .then(resolve)
                .catch(err => {
                    // usar guest
                    Auth.guestCredentials().then(resolve).catch(reject);
                })
        });

        return credPromise.then(credentials => {
            return this._signed(params, credentials);
        });
    }

    /**
    * GET HTTP request
    * @param {string} url - Full request URL
    * @param {JSON} init - Request extra params
    * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
    */
    get(url, init) {
        return this.ajax(url, 'GET', init);
    }

    /**
    * PUT HTTP request
    * @param {String} url - Full request URL
    * @param {JSON} init - Request extra params
    * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
    */
    put(url, init) {
        return this.ajax(url, 'PUT', init);
    }

    /**
    * POST HTTP request
    * @param {String} url - Full request URL
    * @param {JSON} init - Request extra params
    * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
    */
    post(url, init) {
        return this.ajax(url, 'POST', init);
    }

    /**
    * DELETE HTTP request
    * @param {string} url - Full request URL
    * @param {JSON} init - Request extra params
    * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
    */
    del(url, init) {
        return this.ajax(url, 'DELETE', init);
    }

    /**
    * HEAD HTTP request
    * @param {string} url - Full request URL
    * @param {JSON} init - Request extra params
    * @return {Promise} - A promise that resolves to an object with response status and JSON data, if successful.
    */
    head(url, init) {
        return this.ajax(url, 'HEAD', init);
    }

    /**
    * Getting endpoint for API
    * @param {string} apiName - The name of the api
    * @return {string} - The endpoint of the api
    */
    endpoint(apiName) {
        const cloud_logic_array = this._options.endpoints;
        var response = '';
        cloud_logic_array.forEach((v) => {
            if (v.name === apiName) {
                response = v.endpoint;
            }
        });
        return response;
    }

    /** private methods **/

    _signed(params, credentials) {

        let signed_params = Signer.sign(params, {
            secret_key: credentials.secretAccessKey,
            access_key: credentials.accessKeyId,
            session_token: credentials.sessionToken
        });
        if (signed_params.data) {
            signed_params.body = signed_params.data;
        }

        logger.debug(signed_params);

        delete signed_params.headers['host'];

        return axios(signed_params)
            .then(response => response.data)
            .catch((error) => {
                logger.debug(error);
                throw error;
            });
    }

    _unsigned(params) {
        return fetch(params.url, params).then(function (response) {
            return Promise.all([response, response.json()]);
        })
            .then(function (values) {
                return {
                    status: values[0].status,
                    headers: values[0].headers,
                    data: values[1]
                }
            });
    }

    _parseUrl(url) {
        var parts = url.split('/');

        return {
            host: parts[2],
            path: '/' + parts.slice(3).join('/')
        };
    }
};