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

Commit fba8947

Browse files
author
Alan Shaw
authored
feat: add MFS commands write, cp, mkdir (#2)
Additionally, this PR: 1. Adds a `options.qs` option on _all_ endpoints for passing additional query string parameters 2. Documents `options.headers` option (custom HTTP headers) for all endpoints
1 parent 2a8a15b commit fba8947

39 files changed

+661
-149
lines changed

API.md

+252-12
Large diffs are not rendered by default.

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -166,17 +166,17 @@ This module is in heavy development, not all API methods are available (or docum
166166
* dht.put
167167
* dht.query
168168
* dns
169-
* files.cp
169+
* [files.cp](./API.md#filescp)
170170
* files.flush
171171
* files.ls
172172
* files.lsPullStream
173-
* files.mkdir
173+
* [files.mkdir](./API.md#filesmkdir)
174174
* files.mv
175175
* files.read
176176
* files.readPullStream
177177
* files.rm
178178
* files.stat
179-
* files.write
179+
* [files.write](./API.md#fileswrite)
180180
* [id](./API.md#id) TODO: add docs
181181
* key.export
182182
* key.gen

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"main": "src/index.js",
77
"browser": {
88
"./src/lib/configure.js": "./src/lib/configure.browser.js",
9-
"./src/add/form-data.js": "./src/add/form-data.browser.js"
9+
"./src/add/form-data.js": "./src/add/form-data.browser.js",
10+
"./src/files/write/form-data.js": "./src/files/write/form-data.browser.js"
1011
},
1112
"browserslist": ">1.5% or node >=10 and not ios_saf <13 and not ie 11 and not dead",
1213
"files": [
@@ -42,7 +43,7 @@
4243
"bignumber.js": "^9.0.0",
4344
"chai": "^4.2.0",
4445
"dirty-chai": "^2.0.1",
45-
"go-ipfs-dep": "~0.4.21",
46+
"go-ipfs-dep": "^0.4.21",
4647
"interface-ipfs-core": "~0.105.1",
4748
"ipfs-block": "^0.8.1",
4849
"ipfsd-ctl": "^0.43.0",

src/add/form-data.browser.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

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

6-
exports.toFormData = async function toFormData (input) {
6+
exports.toFormData = async (input) => {
77
const files = normaliseInput(input)
88
const formData = new FormData()
99
let i = 0

src/add/form-data.js

+2-26
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
'use strict'
22

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

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

4140
return formData
4241
}
43-
44-
function toStream (iterable) {
45-
let reading = false
46-
return new Readable({
47-
async read (size) {
48-
if (reading) return
49-
reading = true
50-
51-
try {
52-
while (true) {
53-
const { value, done } = await iterable.next(size)
54-
if (done) return this.push(null)
55-
if (!this.push(value)) break
56-
}
57-
} catch (err) {
58-
this.emit('error', err)
59-
if (iterable.return) iterable.return()
60-
} finally {
61-
reading = false
62-
}
63-
}
64-
})
65-
}

src/add/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
2727
'shard-split-threshold': options.shardSplitThreshold,
2828
silent: options.silent,
2929
trickle: options.trickle,
30-
'wrap-with-directory': options.wrapWithDirectory
30+
'wrap-with-directory': options.wrapWithDirectory,
31+
...(options.qs || {})
3132
})
3233

3334
const url = `${apiUrl}${apiPath}/add${qs}`

src/add/normalise-input.js

+1-37
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
'use strict'
22
/* eslint-env browser */
33

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

87
/*
98
Transform one of:
@@ -126,38 +125,3 @@ module.exports = function normalizeInput (input) {
126125
function normalizeTuple ({ path, content }) {
127126
return { path: path || '', content: content ? toAsyncIterable(content) : null }
128127
}
129-
130-
function toAsyncIterable (input) {
131-
// Buffer|ArrayBuffer|TypedArray|array of bytes
132-
if (input[Symbol.iterator]) {
133-
const buf = Buffer.from(input)
134-
return Object.assign(
135-
(async function * () { yield buf })(), // eslint-disable-line require-await
136-
{ length: buf.length }
137-
)
138-
}
139-
140-
// Blob|File
141-
if (typeof Blob !== 'undefined' && input instanceof Blob) {
142-
return Object.assign(
143-
blobToAsyncIterable(input),
144-
{ length: input.size }
145-
)
146-
}
147-
148-
// AsyncIterable<Buffer>
149-
if (input[Symbol.asyncIterator]) {
150-
return (async function * () {
151-
for await (const chunk of input) {
152-
yield Buffer.from(chunk)
153-
}
154-
})()
155-
}
156-
157-
// PullStream
158-
if (typeof input === 'function') {
159-
return toIterator(input)
160-
}
161-
162-
throw new Error('Unexpected input: ' + typeof input)
163-
}

src/bitswap/stat.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
const configure = require('../lib/configure')
44
const { ok } = require('../lib/fetch')
5+
const { objectToQuery } = require('../lib/querystring')
56
const toCamel = require('../lib/to-camel')
67

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

11-
const url = `${apiUrl}${apiPath}/bitswap/stat`
12+
const qs = objectToQuery(options.qs)
13+
const url = `${apiUrl}${apiPath}/bitswap/stat${qs}`
1214
const res = await ok(fetch(url, {
1315
signal: options.signal,
1416
headers: options.headers || headers

src/bitswap/wantlist.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
1313

1414
options = options || {}
1515

16-
const url = `${apiUrl}${apiPath}/bitswap/wantlist${objectToQuery({ peer: peerId })}`
16+
const qs = objectToQuery({
17+
peer: peerId,
18+
...(options.qs || {})
19+
})
20+
21+
const url = `${apiUrl}${apiPath}/bitswap/wantlist${qs}`
1722
const res = await ok(fetch(url, {
1823
signal: options.signal,
1924
headers: options.headers || headers

src/block/get.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
const { Buffer } = require('buffer')
44
const configure = require('../lib/configure')
55
const { ok } = require('../lib/fetch')
6+
const { objectToQuery } = require('../lib/querystring')
67

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

11-
const url = `${apiUrl}${apiPath}/block/get?arg=${encodeURIComponent(cid)}`
12+
const qs = objectToQuery({
13+
arg: cid.toString(),
14+
...(options.qs || {})
15+
})
16+
17+
const url = `${apiUrl}${apiPath}/block/get${qs}`
1218
const res = await ok(fetch(url, {
1319
signal: options.signal,
1420
headers: options.headers || headers

src/block/put.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
1414
format: options.format,
1515
mhtype: options.mhtype,
1616
mhlen: options.mhlen,
17-
pin: options.pin
17+
pin: options.pin,
18+
...(options.qs || {})
1819
})
1920

2021
const url = `${apiUrl}${apiPath}/block/put${qs}`

src/block/stat.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
'use strict'
22

33
const configure = require('../lib/configure')
4+
const { objectToQuery } = require('../lib/querystring')
45
const { ok } = require('../lib/fetch')
56
const toCamel = require('../lib/to-camel')
67

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

11-
const url = `${apiUrl}${apiPath}/block/stat?arg=${encodeURIComponent(cid)}`
12+
const qs = objectToQuery({
13+
arg: cid.toString(),
14+
...(options.qs || {})
15+
})
16+
17+
const url = `${apiUrl}${apiPath}/block/stat${qs}`
1218
const res = await ok(fetch(url, {
1319
signal: options.signal,
1420
headers: options.headers || headers

src/cat.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
const { Buffer } = require('buffer')
44
const configure = require('./lib/configure')
55
const { ok, toIterable } = require('./lib/fetch')
6+
const { objectToQuery } = require('./lib/querystring')
67

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

11-
const url = `${apiUrl}${apiPath}/cat?arg=${encodeURIComponent(cid)}`
12+
const qs = objectToQuery({
13+
arg: cid.toString(),
14+
...(options.qs || {})
15+
})
16+
17+
const url = `${apiUrl}${apiPath}/cat${qs}`
1218
const res = await ok(fetch(url, {
1319
signal: options.signal,
1420
headers: options.headers || headers

src/files/cp.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict'
2+
3+
const configure = require('../lib/configure')
4+
const { ok } = require('../lib/fetch')
5+
const { objectToQuery } = require('../lib/querystring')
6+
7+
module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
8+
return async (...args) => {
9+
let options = {}
10+
11+
if (typeof args[args.length - 1] === 'object') {
12+
options = args.pop()
13+
}
14+
15+
const qs = objectToQuery({
16+
arg: args,
17+
flush: options.flush,
18+
format: options.format,
19+
hash: options.hashAlg,
20+
parents: options.parents,
21+
...(options.qs || {})
22+
})
23+
24+
const url = `${apiUrl}${apiPath}/files/cp${qs}`
25+
const res = await ok(fetch(url, {
26+
signal: options.signal,
27+
headers: options.headers || headers
28+
}))
29+
30+
return res.text()
31+
}
32+
})

src/files/mkdir.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict'
2+
3+
const configure = require('../lib/configure')
4+
const { ok } = require('../lib/fetch')
5+
const { objectToQuery } = require('../lib/querystring')
6+
7+
module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
8+
return async (path, options) => {
9+
options = options || {}
10+
11+
const qs = objectToQuery({
12+
arg: path,
13+
'cid-version': options.cidVersion,
14+
flush: options.flush,
15+
format: options.format,
16+
hash: options.hashAlg,
17+
parents: options.parents,
18+
...(options.qs || {})
19+
})
20+
21+
const url = `${apiUrl}${apiPath}/files/mkdir${qs}`
22+
const res = await ok(fetch(url, {
23+
signal: options.signal,
24+
headers: options.headers || headers
25+
}))
26+
27+
return res.text()
28+
}
29+
})

src/files/write/form-data.browser.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict'
2+
/* eslint-env browser */
3+
4+
const toAsyncIterable = require('../../lib/file-data-to-async-iterable')
5+
6+
exports.toFormData = async (path, input) => {
7+
input = toAsyncIterable(input)
8+
const formData = new FormData()
9+
10+
// In the browser there's _currently_ no streaming upload, buffer up our
11+
// async iterator chunks and append a big Blob :(
12+
// One day, this will be browser streams
13+
const bufs = []
14+
for await (const chunk of input) {
15+
bufs.push(Buffer.isBuffer(chunk) ? chunk.buffer : chunk)
16+
}
17+
18+
formData.append('file', new Blob(bufs, { type: 'application/octet-stream' }))
19+
20+
return formData
21+
}

src/files/write/form-data.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use strict'
2+
3+
const FormData = require('form-data')
4+
const toAsyncIterable = require('../../lib/file-data-to-async-iterable')
5+
const toStream = require('../../lib/iterable-to-readable-stream')
6+
7+
exports.toFormData = (path, input) => {
8+
input = toAsyncIterable(input)
9+
const formData = new FormData()
10+
11+
formData.append(
12+
'file',
13+
// FIXME: add a `path` property to the stream so `form-data` doesn't set
14+
// a Content-Length header that is only the sum of the size of the
15+
// header/footer when knownLength option (below) is null.
16+
Object.assign(toStream(input), { path }),
17+
{
18+
contentType: 'application/octet-stream',
19+
knownLength: input.length // Send Content-Length header if known
20+
}
21+
)
22+
23+
return formData
24+
}

src/files/write/index.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict'
2+
3+
const { objectToQuery } = require('../../lib/querystring')
4+
const configure = require('../../lib/configure')
5+
const { ok } = require('../../lib/fetch')
6+
const { toFormData } = require('./form-data')
7+
8+
module.exports = configure(({ fetch, apiUrl, apiPath, headers }) => {
9+
return async (path, input, options) => {
10+
options = options || {}
11+
12+
const qs = objectToQuery({
13+
arg: path,
14+
'stream-channels': true,
15+
'cid-version': options.cidVersion,
16+
count: options.count,
17+
create: options.create,
18+
hash: options.hashAlg,
19+
offset: options.offset,
20+
parents: options.parents,
21+
'raw-leaves': options.rawLeaves,
22+
truncate: options.truncate,
23+
...(options.qs || {})
24+
})
25+
26+
const url = `${apiUrl}${apiPath}/files/write${qs}`
27+
const res = await ok(fetch(url, {
28+
method: 'POST',
29+
signal: options.signal,
30+
headers: options.headers || headers,
31+
body: await toFormData(path, input)
32+
}))
33+
34+
return res.text()
35+
}
36+
})

0 commit comments

Comments
 (0)