Skip to content
This repository was archived by the owner on Mar 10, 2020. It is now read-only.

Commit da9d17a

Browse files
authored
feat: support UnixFSv1.5 metadata (#1186)
* feat: support UnixFSv1.5 metadata * fix: expose new mfs functions * refactor: send mtime and mode as headers instead of message parts * fix: include headers for directories * chore: update ipfs utils dep version * chore: update ipfs-utils dep * fix: stringify mode in browser * test: add tests for unixfs metadata * fix: fix up tests etc for optional mtime
1 parent 4cd7858 commit da9d17a

17 files changed

+429
-17
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"explain-error": "^1.0.4",
5555
"form-data": "^3.0.0",
5656
"ipfs-block": "~0.8.1",
57-
"ipfs-utils": "^0.4.0",
57+
"ipfs-utils": "^0.4.2",
5858
"ipld-dag-cbor": "~0.15.0",
5959
"ipld-dag-pb": "^0.18.1",
6060
"ipld-raw": "^4.0.0",
@@ -84,7 +84,7 @@
8484
"cross-env": "^6.0.0",
8585
"detect-node": "^2.0.4",
8686
"go-ipfs-dep": "^0.4.22",
87-
"interface-ipfs-core": "~0.125.0",
87+
"interface-ipfs-core": "^0.126.0",
8888
"ipfsd-ctl": "^1.0.0",
8989
"ndjson": "^1.5.0",
9090
"nock": "^11.4.0",

src/add/form-data.browser.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,29 @@
22
/* eslint-env browser */
33

44
const normaliseInput = require('ipfs-utils/src/files/normalise-input')
5+
const mtimeToObject = require('../lib/mtime-to-object')
56

67
exports.toFormData = async input => {
78
const files = normaliseInput(input)
89
const formData = new FormData()
910
let i = 0
1011

1112
for await (const file of files) {
13+
const headers = {}
14+
15+
if (file.mtime !== undefined && file.mtime !== null) {
16+
const mtime = mtimeToObject(file.mtime)
17+
18+
if (mtime) {
19+
headers.mtime = mtime.secs
20+
headers['mtime-nsecs'] = mtime.nsecs
21+
}
22+
}
23+
24+
if (file.mode !== undefined && file.mode !== null) {
25+
headers.mode = file.mode.toString(8).padStart(4, '0')
26+
}
27+
1228
if (file.content) {
1329
// In the browser there's _currently_ no streaming upload, buffer up our
1430
// async iterator chunks and append a big Blob :(
@@ -18,9 +34,13 @@ exports.toFormData = async input => {
1834
bufs.push(chunk)
1935
}
2036

21-
formData.append(`file-${i}`, new Blob(bufs, { type: 'application/octet-stream' }), encodeURIComponent(file.path))
37+
formData.append(`file-${i}`, new Blob(bufs, { type: 'application/octet-stream' }), encodeURIComponent(file.path), {
38+
header: headers
39+
})
2240
} else {
23-
formData.append(`dir-${i}`, new Blob([], { type: 'application/x-directory' }), encodeURIComponent(file.path))
41+
formData.append(`dir-${i}`, new Blob([], { type: 'application/x-directory' }), encodeURIComponent(file.path), {
42+
header: headers
43+
})
2444
}
2545

2646
i++

src/add/form-data.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,29 @@ const { Buffer } = require('buffer')
55
const toStream = require('it-to-stream')
66
const normaliseInput = require('ipfs-utils/src/files/normalise-input')
77
const { isElectronRenderer } = require('ipfs-utils/src/env')
8+
const mtimeToObject = require('../lib/mtime-to-object')
89

910
exports.toFormData = async input => {
1011
const files = normaliseInput(input)
1112
const formData = new FormData()
1213
let i = 0
1314

1415
for await (const file of files) {
16+
const headers = {}
17+
18+
if (file.mtime !== undefined && file.mtime !== null) {
19+
const mtime = mtimeToObject(file.mtime)
20+
21+
if (mtime) {
22+
headers.mtime = mtime.secs
23+
headers['mtime-nsecs'] = mtime.nsecs
24+
}
25+
}
26+
27+
if (file.mode !== undefined && file.mode !== null) {
28+
headers.mode = file.mode.toString(8).padStart(4, '0')
29+
}
30+
1531
if (file.content) {
1632
// In Node.js, FormData can be passed a stream so no need to buffer
1733
formData.append(
@@ -26,13 +42,15 @@ exports.toFormData = async input => {
2642
{
2743
filepath: encodeURIComponent(file.path),
2844
contentType: 'application/octet-stream',
29-
knownLength: file.content.length // Send Content-Length header if known
45+
knownLength: file.content.length, // Send Content-Length header if known
46+
header: headers
3047
}
3148
)
3249
} else {
3350
formData.append(`dir-${i}`, Buffer.alloc(0), {
3451
filepath: encodeURIComponent(file.path),
35-
contentType: 'application/x-directory'
52+
contentType: 'application/x-directory',
53+
header: headers
3654
})
3755
}
3856

src/add/index.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ module.exports = configure(({ ky }) => {
5252
}
5353
})
5454

55-
function toCoreInterface ({ name, hash, size }) {
56-
return { path: name, hash, size: parseInt(size) }
55+
function toCoreInterface ({ name, hash, size, mode, mtime }) {
56+
const output = {
57+
path: name,
58+
hash,
59+
size: parseInt(size)
60+
}
61+
62+
if (mode !== undefined) {
63+
output.mode = parseInt(mode, 8)
64+
}
65+
66+
return output
5767
}

src/files/chmod.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict'
2+
3+
const configure = require('../lib/configure')
4+
const modeToString = require('../lib/mode-to-string')
5+
6+
module.exports = configure(({ ky }) => {
7+
return function chmod (path, mode, options) {
8+
options = options || {}
9+
10+
const searchParams = new URLSearchParams(options.searchParams)
11+
searchParams.append('arg', path)
12+
searchParams.append('mode', modeToString(mode))
13+
if (options.format) searchParams.set('format', options.format)
14+
if (options.flush != null) searchParams.set('flush', options.flush)
15+
if (options.hashAlg) searchParams.set('hash', options.hashAlg)
16+
if (options.parents != null) searchParams.set('parents', options.parents)
17+
18+
return ky.post('files/chmod', {
19+
timeout: options.timeout,
20+
signal: options.signal,
21+
headers: options.headers,
22+
searchParams
23+
}).text()
24+
}
25+
})

src/files/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module.exports = config => {
88
const read = require('./read')(config)
99

1010
return {
11+
chmod: callbackify.variadic(require('./chmod')(config)),
1112
cp: callbackify.variadic(require('./cp')(config)),
1213
mkdir: callbackify.variadic(require('./mkdir')(config)),
1314
flush: callbackify.variadic(require('./flush')(config)),
@@ -19,6 +20,7 @@ module.exports = config => {
1920
read: callbackify.variadic(concatify(read)),
2021
readReadableStream: streamify.readable(read),
2122
readPullStream: pullify.source(read),
23+
touch: callbackify.variadic(require('./touch')(config)),
2224
write: callbackify.variadic(require('./write')(config)),
2325
mv: callbackify.variadic(require('./mv')(config))
2426
}

src/files/ls.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const CID = require('cids')
44
const ndjson = require('iterable-ndjson')
55
const toIterable = require('../lib/stream-to-iterable')
66
const configure = require('../lib/configure')
7-
const toCamel = require('../lib/object-to-camel')
7+
const toCamelWithMetadata = require('../lib/object-to-camel-with-metadata')
88

99
module.exports = configure(({ ky }) => {
1010
return async function * ls (path, options) {
@@ -32,11 +32,12 @@ module.exports = configure(({ ky }) => {
3232
// go-ipfs does not yet support the "stream" option
3333
if ('Entries' in result) {
3434
for (const entry of result.Entries || []) {
35-
yield toCamel(entry)
35+
yield toCamelWithMetadata(entry)
3636
}
3737
return
3838
}
39-
yield toCamel(result)
39+
40+
yield toCamelWithMetadata(result)
4041
}
4142
}
4243
})

src/files/mkdir.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
'use strict'
22

33
const configure = require('../lib/configure')
4+
const modeToString = require('../lib/mode-to-string')
5+
const mtimeToObject = require('../lib/mtime-to-object')
46

57
module.exports = configure(({ ky }) => {
68
return (path, options) => {
79
options = options || {}
10+
const mtime = mtimeToObject(options.mtime)
811

912
const searchParams = new URLSearchParams(options.searchParams)
1013
searchParams.append('arg', path)
@@ -13,6 +16,14 @@ module.exports = configure(({ ky }) => {
1316
if (options.flush != null) searchParams.set('flush', options.flush)
1417
if (options.hashAlg) searchParams.set('hash', options.hashAlg)
1518
if (options.parents != null) searchParams.set('parents', options.parents)
19+
if (mtime) {
20+
searchParams.set('mtime', mtime.secs)
21+
22+
if (mtime.nsecs != null) {
23+
searchParams.set('mtimeNsecs', mtime.nsecs)
24+
}
25+
}
26+
if (options.mode != null) searchParams.set('mode', modeToString(options.mode))
1627

1728
return ky.post('files/mkdir', {
1829
timeout: options.timeout,

src/files/stat.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict'
22

33
const configure = require('../lib/configure')
4-
const toCamel = require('../lib/object-to-camel')
4+
const toCamelWithMetadata = require('../lib/object-to-camel-with-metadata')
55

66
module.exports = configure(({ ky }) => {
77
return async (path, options) => {
@@ -27,6 +27,7 @@ module.exports = configure(({ ky }) => {
2727
}).json()
2828

2929
res.WithLocality = res.WithLocality || false
30-
return toCamel(res)
30+
31+
return toCamelWithMetadata(res)
3132
}
3233
})

src/files/touch.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict'
2+
3+
const configure = require('../lib/configure')
4+
const mtimeToObject = require('../lib/mtime-to-object')
5+
6+
module.exports = configure(({ ky }) => {
7+
return function touch (path, options) {
8+
options = options || {}
9+
const mtime = mtimeToObject(options.mtime)
10+
11+
const searchParams = new URLSearchParams(options.searchParams)
12+
searchParams.append('arg', path)
13+
if (mtime) {
14+
searchParams.set('mtime', mtime.secs)
15+
searchParams.set('mtimeNsecs', mtime.nsecs)
16+
}
17+
if (options.format) searchParams.set('format', options.format)
18+
if (options.flush != null) searchParams.set('flush', options.flush)
19+
if (options.hashAlg) searchParams.set('hash', options.hashAlg)
20+
if (options.parents != null) searchParams.set('parents', options.parents)
21+
22+
return ky.post('files/touch', {
23+
timeout: options.timeout,
24+
signal: options.signal,
25+
headers: options.headers,
26+
searchParams
27+
}).text()
28+
}
29+
})

src/files/write.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
const configure = require('../lib/configure')
44
const toFormData = require('../lib/buffer-to-form-data')
5+
const modeToString = require('../lib/mode-to-string')
6+
const mtimeToObject = require('../lib/mtime-to-object')
57

68
module.exports = configure(({ ky }) => {
79
return async (path, input, options) => {
810
options = options || {}
11+
const mtime = mtimeToObject(options.mtime)
912

1013
const searchParams = new URLSearchParams(options.searchParams)
1114
searchParams.set('arg', path)
@@ -18,13 +21,24 @@ module.exports = configure(({ ky }) => {
1821
if (options.parents != null) searchParams.set('parents', options.parents)
1922
if (options.rawLeaves != null) searchParams.set('raw-leaves', options.rawLeaves)
2023
if (options.truncate != null) searchParams.set('truncate', options.truncate)
24+
if (mtime) {
25+
searchParams.set('mtime', mtime.secs)
26+
27+
if (mtime.nsecs != null) {
28+
searchParams.set('mtimeNsecs', mtime.nsecs)
29+
}
30+
}
2131

2232
const res = await ky.post('files/write', {
2333
timeout: options.timeout,
2434
signal: options.signal,
2535
headers: options.headers,
2636
searchParams,
27-
body: toFormData(input) // TODO: support inputs other than buffer as per spec
37+
body: toFormData(input, {
38+
mode: options.mode != null ? modeToString(options.mode) : undefined,
39+
mtime: mtime ? mtime.secs : undefined,
40+
mtimeNsecs: mtime ? mtime.nsecs : undefined
41+
}) // TODO: support inputs other than buffer as per spec
2842
})
2943

3044
return res.text()

src/lib/buffer-to-form-data.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,25 @@
33
const FormData = require('form-data')
44
const { isElectronRenderer } = require('ipfs-utils/src/env')
55

6-
module.exports = buf => {
6+
module.exports = (buf, { mode, mtime, mtimeNsecs } = {}) => {
7+
const headers = {}
8+
9+
if (mode != null) {
10+
headers.mode = mode
11+
}
12+
13+
if (mtime != null) {
14+
headers.mtime = mtime
15+
16+
if (mtimeNsecs != null) {
17+
headers['mtime-nsecs'] = mtimeNsecs
18+
}
19+
}
20+
721
const formData = new FormData()
8-
formData.append('file', buf)
22+
formData.append('file', buf, {
23+
header: headers
24+
})
925
return formData
1026
}
1127

src/lib/mode-to-string.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict'
2+
3+
module.exports = (mode) => {
4+
if (mode === undefined || mode === null) {
5+
return undefined
6+
}
7+
8+
if (typeof mode === 'string' || mode instanceof String) {
9+
return mode
10+
}
11+
12+
return mode.toString(8).padStart(4, '0')
13+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy