Skip to content
This repository was archived by the owner on Jun 30, 2023. It is now read-only.

feat: add MFS commands write, cp, mkdir #2

Merged
merged 1 commit into from
Jul 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 252 additions & 12 deletions API.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,17 @@ This module is in heavy development, not all API methods are available (or docum
* dht.put
* dht.query
* dns
* files.cp
* [files.cp](./API.md#filescp)
* files.flush
* files.ls
* files.lsPullStream
* files.mkdir
* [files.mkdir](./API.md#filesmkdir)
* files.mv
* files.read
* files.readPullStream
* files.rm
* files.stat
* files.write
* [files.write](./API.md#fileswrite)
* [id](./API.md#id) TODO: add docs
* key.export
* key.gen
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"main": "src/index.js",
"browser": {
"./src/lib/configure.js": "./src/lib/configure.browser.js",
"./src/add/form-data.js": "./src/add/form-data.browser.js"
"./src/add/form-data.js": "./src/add/form-data.browser.js",
"./src/files/write/form-data.js": "./src/files/write/form-data.browser.js"
},
"browserslist": ">1.5% or node >=10 and not ios_saf <13 and not ie 11 and not dead",
"files": [
Expand Down Expand Up @@ -42,7 +43,7 @@
"bignumber.js": "^9.0.0",
"chai": "^4.2.0",
"dirty-chai": "^2.0.1",
"go-ipfs-dep": "~0.4.21",
"go-ipfs-dep": "^0.4.21",
"interface-ipfs-core": "~0.105.1",
"ipfs-block": "^0.8.1",
"ipfsd-ctl": "^0.43.0",
Expand Down
2 changes: 1 addition & 1 deletion src/add/form-data.browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

const normaliseInput = require('./normalise-input')

exports.toFormData = async function toFormData (input) {
exports.toFormData = async (input) => {
const files = normaliseInput(input)
const formData = new FormData()
let i = 0
Expand Down
28 changes: 2 additions & 26 deletions src/add/form-data.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
'use strict'

// const toStream = require('async-iterator-to-stream')
const FormData = require('form-data')
const { Readable } = require('stream')
const normaliseInput = require('./normalise-input')
const toStream = require('../lib/iterable-to-readable-stream')

exports.toFormData = async function toFormData (input) {
exports.toFormData = async (input) => {
// In Node.js, FormData can be passed a stream so no need to buffer
const files = normaliseInput(input)
const formData = new FormData()
Expand Down Expand Up @@ -40,26 +39,3 @@ exports.toFormData = async function toFormData (input) {

return formData
}

function toStream (iterable) {
let reading = false
return new Readable({
async read (size) {
if (reading) return
reading = true

try {
while (true) {
const { value, done } = await iterable.next(size)
if (done) return this.push(null)
if (!this.push(value)) break
}
} catch (err) {
this.emit('error', err)
if (iterable.return) iterable.return()
} finally {
reading = false
}
}
})
}
3 changes: 2 additions & 1 deletion src/add/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
'shard-split-threshold': options.shardSplitThreshold,
silent: options.silent,
trickle: options.trickle,
'wrap-with-directory': options.wrapWithDirectory
'wrap-with-directory': options.wrapWithDirectory,
...(options.qs || {})
})

const url = `${apiUrl}${apiPath}/add${qs}`
Expand Down
38 changes: 1 addition & 37 deletions src/add/normalise-input.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
'use strict'
/* eslint-env browser */

const toIterator = require('pull-stream-to-async-iterator')
const { Buffer } = require('buffer')
const blobToAsyncIterable = require('../lib/blob-to-async-iterable')
const toAsyncIterable = require('../lib/file-data-to-async-iterable')

/*
Transform one of:
Expand Down Expand Up @@ -126,38 +125,3 @@ module.exports = function normalizeInput (input) {
function normalizeTuple ({ path, content }) {
return { path: path || '', content: content ? toAsyncIterable(content) : null }
}

function toAsyncIterable (input) {
// Buffer|ArrayBuffer|TypedArray|array of bytes
if (input[Symbol.iterator]) {
const buf = Buffer.from(input)
return Object.assign(
(async function * () { yield buf })(), // eslint-disable-line require-await
{ length: buf.length }
)
}

// Blob|File
if (typeof Blob !== 'undefined' && input instanceof Blob) {
return Object.assign(
blobToAsyncIterable(input),
{ length: input.size }
)
}

// AsyncIterable<Buffer>
if (input[Symbol.asyncIterator]) {
return (async function * () {
for await (const chunk of input) {
yield Buffer.from(chunk)
}
})()
}

// PullStream
if (typeof input === 'function') {
return toIterator(input)
}

throw new Error('Unexpected input: ' + typeof input)
}
4 changes: 3 additions & 1 deletion src/bitswap/stat.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

const configure = require('../lib/configure')
const { ok } = require('../lib/fetch')
const { objectToQuery } = require('../lib/querystring')
const toCamel = require('../lib/to-camel')

module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
return async (options) => {
options = options || {}

const url = `${apiUrl}${apiPath}/bitswap/stat`
const qs = objectToQuery(options.qs)
const url = `${apiUrl}${apiPath}/bitswap/stat${qs}`
const res = await ok(fetch(url, {
signal: options.signal,
headers: options.headers || headers
Expand Down
7 changes: 6 additions & 1 deletion src/bitswap/wantlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {

options = options || {}

const url = `${apiUrl}${apiPath}/bitswap/wantlist${objectToQuery({ peer: peerId })}`
const qs = objectToQuery({
peer: peerId,
...(options.qs || {})
})

const url = `${apiUrl}${apiPath}/bitswap/wantlist${qs}`
const res = await ok(fetch(url, {
signal: options.signal,
headers: options.headers || headers
Expand Down
8 changes: 7 additions & 1 deletion src/block/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
const { Buffer } = require('buffer')
const configure = require('../lib/configure')
const { ok } = require('../lib/fetch')
const { objectToQuery } = require('../lib/querystring')

module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
return async (cid, options) => {
options = options || {}

const url = `${apiUrl}${apiPath}/block/get?arg=${encodeURIComponent(cid)}`
const qs = objectToQuery({
arg: cid.toString(),
...(options.qs || {})
})

const url = `${apiUrl}${apiPath}/block/get${qs}`
const res = await ok(fetch(url, {
signal: options.signal,
headers: options.headers || headers
Expand Down
3 changes: 2 additions & 1 deletion src/block/put.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
format: options.format,
mhtype: options.mhtype,
mhlen: options.mhlen,
pin: options.pin
pin: options.pin,
...(options.qs || {})
})

const url = `${apiUrl}${apiPath}/block/put${qs}`
Expand Down
8 changes: 7 additions & 1 deletion src/block/stat.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
'use strict'

const configure = require('../lib/configure')
const { objectToQuery } = require('../lib/querystring')
const { ok } = require('../lib/fetch')
const toCamel = require('../lib/to-camel')

module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
return async (cid, options) => {
options = options || {}

const url = `${apiUrl}${apiPath}/block/stat?arg=${encodeURIComponent(cid)}`
const qs = objectToQuery({
arg: cid.toString(),
...(options.qs || {})
})

const url = `${apiUrl}${apiPath}/block/stat${qs}`
const res = await ok(fetch(url, {
signal: options.signal,
headers: options.headers || headers
Expand Down
8 changes: 7 additions & 1 deletion src/cat.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
const { Buffer } = require('buffer')
const configure = require('./lib/configure')
const { ok, toIterable } = require('./lib/fetch')
const { objectToQuery } = require('./lib/querystring')

module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
return (cid, options) => (async function * () {
options = options || {}

const url = `${apiUrl}${apiPath}/cat?arg=${encodeURIComponent(cid)}`
const qs = objectToQuery({
arg: cid.toString(),
...(options.qs || {})
})

const url = `${apiUrl}${apiPath}/cat${qs}`
const res = await ok(fetch(url, {
signal: options.signal,
headers: options.headers || headers
Expand Down
32 changes: 32 additions & 0 deletions src/files/cp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict'

const configure = require('../lib/configure')
const { ok } = require('../lib/fetch')
const { objectToQuery } = require('../lib/querystring')

module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
return async (...args) => {
let options = {}

if (typeof args[args.length - 1] === 'object') {
options = args.pop()
}

const qs = objectToQuery({
arg: args,
flush: options.flush,
format: options.format,
hash: options.hashAlg,
parents: options.parents,
...(options.qs || {})
})

const url = `${apiUrl}${apiPath}/files/cp${qs}`
const res = await ok(fetch(url, {
signal: options.signal,
headers: options.headers || headers
}))

return res.text()
}
})
29 changes: 29 additions & 0 deletions src/files/mkdir.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict'

const configure = require('../lib/configure')
const { ok } = require('../lib/fetch')
const { objectToQuery } = require('../lib/querystring')

module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
return async (path, options) => {
options = options || {}

const qs = objectToQuery({
arg: path,
'cid-version': options.cidVersion,
flush: options.flush,
format: options.format,
hash: options.hashAlg,
parents: options.parents,
...(options.qs || {})
})

const url = `${apiUrl}${apiPath}/files/mkdir${qs}`
const res = await ok(fetch(url, {
signal: options.signal,
headers: options.headers || headers
}))

return res.text()
}
})
21 changes: 21 additions & 0 deletions src/files/write/form-data.browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict'
/* eslint-env browser */

const toAsyncIterable = require('../../lib/file-data-to-async-iterable')

exports.toFormData = async (path, input) => {
input = toAsyncIterable(input)
const formData = new FormData()

// In the browser there's _currently_ no streaming upload, buffer up our
// async iterator chunks and append a big Blob :(
// One day, this will be browser streams
const bufs = []
for await (const chunk of input) {
bufs.push(Buffer.isBuffer(chunk) ? chunk.buffer : chunk)
}

formData.append('file', new Blob(bufs, { type: 'application/octet-stream' }))

return formData
}
24 changes: 24 additions & 0 deletions src/files/write/form-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict'

const FormData = require('form-data')
const toAsyncIterable = require('../../lib/file-data-to-async-iterable')
const toStream = require('../../lib/iterable-to-readable-stream')

exports.toFormData = (path, input) => {
input = toAsyncIterable(input)
const formData = new FormData()

formData.append(
'file',
// FIXME: add a `path` property to the stream so `form-data` doesn't set
// a Content-Length header that is only the sum of the size of the
// header/footer when knownLength option (below) is null.
Object.assign(toStream(input), { path }),
{
contentType: 'application/octet-stream',
knownLength: input.length // Send Content-Length header if known
}
)

return formData
}
36 changes: 36 additions & 0 deletions src/files/write/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict'

const { objectToQuery } = require('../../lib/querystring')
const configure = require('../../lib/configure')
const { ok } = require('../../lib/fetch')
const { toFormData } = require('./form-data')

module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
return async (path, input, options) => {
options = options || {}

const qs = objectToQuery({
arg: path,
'stream-channels': true,
'cid-version': options.cidVersion,
count: options.count,
create: options.create,
hash: options.hashAlg,
offset: options.offset,
parents: options.parents,
'raw-leaves': options.rawLeaves,
truncate: options.truncate,
...(options.qs || {})
})

const url = `${apiUrl}${apiPath}/files/write${qs}`
const res = await ok(fetch(url, {
method: 'POST',
signal: options.signal,
headers: options.headers || headers,
body: await toFormData(path, input)
}))

return res.text()
}
})
Loading