Source: util.js

const fs = require('fs')
const path = require('path')
const crypto = require('crypto')
const FormData = require('form-data')

const { ERRORS } = require('./consts')
/**
 * @typedef {object} Util
 * @property {function} getFormData - Method for getting form data from formFields and files
 * @property {function} readFile - Method for reading files
 * @property {function} verifyResponse - Method for verifying responses
 */

/**
 * Celsius js SDK Util
 * @module celsius-js-sdk/util
 */

/**
 * Creates and configures Util
 *
 * @function
 *
 * @param {object} config - Contains configuration data
 * @returns {Util}
 */
const Util = function (config) {
  const Util = this
  Util.config = config

  return {
    /**
     * Creates and returns form data from provided form fields and files
     *
     * @function
     *
     * @param {object} formFields - Object containing form fields
     * @param {object} files - Object containing files. Keys are field names, values are paths
     * @returns {Promise} FormData that is to be sent with a request
     */
    getFormData: function (formFields, files) {
      return new Promise((resolve, reject) => {
        const formData = new FormData({ maxDataSize: 10485760 })

        for (const key in formFields) {
          if (formFields.hasOwnProperty(key)) {
            formData.append(key, formFields[key])
          }
        }

        const promises = []
        const fileMetadata = []

        for (const key in files) {
          if (files.hasOwnProperty(key)) {
            const filePath = files[key]
            fileMetadata.push({
              name: path.basename(filePath),
              fieldName: key
            })
            promises.push(this.readFile(filePath))
          }
        }

        Promise.all(promises).then((results) => {
          for (let i = 0; i < results.length; i++) {
            const metadata = fileMetadata[i]
            formData.append(metadata.fieldName, results[i], {
              filename: metadata.name
            })
          }
          resolve(formData)
        })
      })
    },

    /**
     * Reads a file asynchronously.
     *
     * @function readFile
     *
     * @param {string} filePath - Path to the file
     * @returns {Promise} Contents of a file stored in a buffer
     */
    readFile: function (filePath) {
      return new Promise((resolve, reject) => {
        const stream = fs.createReadStream(filePath)

        const chunks = []

        stream.on('data', function (chunk) {
          chunks.push(chunk)
        })

        stream.on('end', function () {
          return resolve(Buffer.concat(chunks))
        })
      })
    },

    /**
     * Verifies responses received from api.
     *
     * @function verifyResponse
     *
     * @param {object} data - Response data
     * @param {string} signature - Header containing signature
     * @returns {boolean} Result of verification
     */
    verifyResponse: function (data, signature) {
      let publicKey = Util.config.publicKey
      if (!publicKey) {
        throw new Error(ERRORS.NO_PUBLIC_KEY)
      }
      publicKey = Buffer.from(publicKey, 'base64').toString()
      return crypto.createVerify('sha256').update(JSON.stringify(data)).verify(publicKey, signature, 'base64')
    }
  }
}

class ValidationError {
  constructor (error) {
    this.status = error.status

    if (error.data.message) {
      this.message = error.data.message
    } else {
      this.message = error.data
    }
  }
}

module.exports = {
  Util,
  ValidationError
}