pnut.js

require("es6-promise").polyfill();
require("isomorphic-fetch");
const FormData = require("form-data");

const snakeCase = require("snake-case");
const appendQuery = require("append-query");

const authentication = require("./authentication");
const users = require("./users");
const posts = require("./posts");
const explore = require("./explore");
const channels = require("./channels");
const streamMarkers = require("./stream_markers");
const messages = require("./messages");
const system = require("./system");
const appStreams = require("./app_streams");
const clients = require("./clients");
const files = require("./files");
const polls = require("./polls");

/**
 * pnut-butter.js
 *
 * Wrapper library for the pnut.io API.
 *
 * @returns {Object}
 */
const pnut = () => {
  const api = {
    base: "https://api.pnut.io/v0",
    streamBase: "wss://stream.pnut.io/v0",
    token: "",

    request(
      endpoint,
      {
        httpMethod = "GET",
        params = {},
        resultAs = "json",
        dataAs = "json",
        data = {}
      } = {}
    ) {
      let auth = {};
      if (this.token !== "") {
        auth = {
          Authorization: `Bearer ${this.token}`
        };
      }

      let options = {
        method: httpMethod,
        headers: auth
      };

      if (httpMethod !== "GET" && Object.keys(data).length > 0) {
        if (dataAs === "json") {
          options.body = JSON.stringify(data);
          options.headers["Content-Type"] = "application/json";
        } else if (dataAs === "form") {
          let form = new FormData();
          Object.keys(data).forEach(key => form.append(key, data[key]));
          options.body = form;
        } else if (dataAs === "dom-node") {
          let form = new FormData(data);
          options.body = form;
        }
      }

      /**
       * We do some magic here to allow any arbitrary URI parameter written
       * camelCase, snake_case or any-other-case too.
       * TODO: This should be extract into a separate module that handles the
       * URL construction.
       */
      const sanitizedParams = {};
      Object.keys(params).forEach(key => {
        if (Array.isArray(params[key])) {
          sanitizedParams[snakeCase(key)] = params[key].join(",");
        } else {
          sanitizedParams[snakeCase(key)] = params[key];
        }
      });
      const uri = appendQuery(`${this.base}${endpoint}`, sanitizedParams, {
        encodeComponents: false
      });

      return fetch(uri, options).then(function(response) {
        if (resultAs === "response") {
          return response;
        } else {
          return response[resultAs]();
        }
      });
    }
  };

  return Object.assign(
    {
      /**
       * Set auth token.
       * @param {string} newToken - API access token
       */
      set token(newToken) {
        api.token = newToken;
      },

      /**
       * Return the auth token that was set.
       * @returns {string}
       */
      get token() {
        return api.token;
      },

      /**
       * Send request with custom endpoint / parameters.
       * @param {string} endpoint - The custom call
       * @param {string} method - HTTP verb, defaults to GET
       * @return {Promise}
       */
      custom(endpoint, method = "GET", data = {}) {
        endpoint = endpoint.startsWith("/") ? `${endpoint}` : `/${endpoint}`;
        return api.request(`${endpoint}`, {
          httpMethod: method,
          data: data
        });
      }
    },
    authentication(api),
    users(api),
    posts(api),
    explore(api),
    channels(api),
    streamMarkers(api),
    messages(api),
    system(api),
    appStreams(api),
    clients(api),
    files(api),
    polls(api)
  );
};

module.exports = pnut();