All files / client/files add.js

91.21% Statements 83/91
80.56% Branches 29/36
95% Functions 19/20
95.06% Lines 77/81

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 1581x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     39x   39x   35x 31x     31x 2x 2x     31x 38x     31x           62x       31x       31x       1x 1x 1x     8x 8x   1x 1x 1x 10x 1x           39x   39x 2x 2x 2x     2x         2x           2x 2x 2x         2x 2x     2x 9x               39x       2x 2x 7x 7x 7x         107x   107x 51x 51x 56x 2x 54x 3x 51x 39x     12x     1x 5x 5x   5x       10x 5x 5x     10x         5x    
import { caller } from 'postmsg-rpc'
import callbackify from 'callbackify'
import { Transform } from 'stream'
import pull from 'pull-stream'
import PMS from 'pull-postmsg-stream'
import toPull from 'stream-to-pull-stream'
import isStream from 'is-stream'
import { isSource } from 'is-pull-stream'
import shortid from 'shortid'
import { pre } from 'prepost'
import defer from 'pull-defer'
import Abortable from 'pull-abortable'
import { isBuffer, bufferToJson } from '../../serialization/buffer'
import { functionToJson } from '../../serialization/function'
 
export default function (opts) {
  const api = {
    add: (() => {
      const add = callbackify.variadic(
        pre(
          (...args) => {
            const fileToJsonOpts = { pms: opts }
 
            // FIXME: implement progress properly
            if (args[1] && args[1].progress) {
              fileToJsonOpts.onProgressIncrement = createOnProgressIncrement(args[1].progress)
              delete args[1].progress
            }
 
            args[0] = Array.isArray(args[0])
              ? args[0].map((file) => fileToJson(file, fileToJsonOpts))
              : fileToJson(args[0], fileToJsonOpts)
 
            return args
          },
          caller('ipfs.files.add', opts)
        )
      )
 
      return (...args) => {
        // Pull streams are just functions and so callbackify.variadic thinks
        // the stream is a callback function! Instead explicitly pass null for
        // the options arg.
        Iif (args.length === 1 && isSource(args[0])) {
          args = args.concat(null)
        }
 
        return add(...args)
      }
    })(),
    // FIXME: implement add readable stream properly
    addReadableStream (...args) {
      const content = []
      return new Transform({
        objectMode: true,
        transform (file, enc, cb) {
          content.push(file)
          cb()
        },
        flush (cb) {
          api.add.apply(api, [content].concat(args, (err, res) => {
            Iif (err) return cb(err)
            res.forEach((file) => this.push(file))
            cb()
          }))
        }
      })
    },
    addPullStream: (() => {
      const addPullStream = caller('ipfs.files.addPullStream', opts)
 
      return (...args) => {
        const deferred = defer.source()
        const abortable = Abortable()
        const fileToJsonOpts = { pms: opts }
 
        // FIXME: implement progress properly
        Iif (args[0] && args[0].progress) {
          fileToJsonOpts.onProgressIncrement = createOnProgressIncrement(args[0].progress)
          delete args[0].progress
        }
 
        const readFnName = shortid()
 
        // Create the through stream what will connect the client to the
        // server, our source is deferred, until the server responds to tell
        // us the name of the read function we can use to pull added file
        // info from.
        const through = function (read) {
          PMS.sink(readFnName, opts)(read)
          return deferred
        }
 
        // Call addPullStream on the server, sending the name of the read
        // function it can use to pull files to add from.
        addPullStream(functionToJson(readFnName), ...args)
          .then((res) => deferred.resolve(PMS.source(res.name, opts)))
          .catch((err) => abortable.abort(err))
 
        return pull(
          pull.map((file) => fileToJson(file, fileToJsonOpts)),
          through,
          abortable
        )
      }
    })()
  }
 
  return api
}
 
function createOnProgressIncrement (onProgress) {
  let bytes = 0
  return (incrementBytes) => {
    bytes += incrementBytes
    onProgress(bytes)
    return bytes
  }
}
 
function fileToJson (file, opts) {
  opts = opts || {}
 
  if (isBuffer(file)) { // Buffer
    if (opts.onProgressIncrement) opts.onProgressIncrement(file.length)
    return bufferToJson(file)
  } else if (isStream.readable(file)) { // Node stream
    return pullStreamToJson(toPull.source(file), opts)
  } else if (isSource(file)) { // Pull stream
    return pullStreamToJson(file, opts)
  } else if (file && file.content) { // Object { path?, content }
    return Object.assign({}, file, { content: fileToJson(file.content, opts) })
  }
 
  return file // Object { path } maybe, but could be anything
}
 
const pullStreamToJson = (source, opts) => {
  opts = opts || {}
  const readFnName = shortid()
 
  pull(
    source,
    PMS.sink(readFnName, Object.assign({}, opts.pms, {
      post (res) {
        if (isBuffer(res.data)) {
          Iif (opts.onProgressIncrement) opts.onProgressIncrement(res.data.length)
          res.data = bufferToJson(res.data)
        }
 
        return res
      }
    }))
  )
 
  return functionToJson(readFnName)
}