Skip to content

Commit 8c2f42a

Browse files
committed
feat: streams in browser and Bzz API revamp
1 parent e1c34a4 commit 8c2f42a

File tree

27 files changed

+1776
-454
lines changed

27 files changed

+1776
-454
lines changed

__tests__/api-bzz-node.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import * as os from 'os'
66
import * as path from 'path'
7+
import { Readable } from 'stream'
8+
import * as crypto from 'crypto'
79
import * as fs from 'fs-extra'
810
import { Subject } from 'rxjs'
911
import * as tar from 'tar-stream'
@@ -68,6 +70,30 @@ describe('api-bzz-node', () => {
6870
expect(await response.text()).toBe(uploadContent)
6971
})
7072

73+
it('uploading and downloading single file using bzz and streams', async () => {
74+
const value = crypto.randomBytes(60).toString('hex')
75+
const s = new Readable()
76+
s.push(value)
77+
s.push(null)
78+
79+
const manifestHash = await bzz.upload(s, {
80+
contentType: 'text/plain',
81+
})
82+
83+
const data: Array<Uint8Array> = []
84+
const responseStream = await bzz.downloadStream(manifestHash)
85+
responseStream.on('data', (d: Uint8Array) => {
86+
data.push(d)
87+
})
88+
89+
return new Promise(resolve => {
90+
responseStream.on('end', () => {
91+
expect(Buffer.concat(data).toString()).toBe(value)
92+
resolve()
93+
})
94+
})
95+
})
96+
7197
it('uploading and downloading single file using bzz with content path', async () => {
7298
const manifestHash = await bzz.upload(uploadContent, {
7399
contentType: 'text/plain',
@@ -84,6 +110,30 @@ describe('api-bzz-node', () => {
84110
expect(await response.text()).toBe(uploadContent)
85111
})
86112

113+
it('uploading and downloading single file using bzz-raw and streams', async () => {
114+
const value = crypto.randomBytes(60).toString('hex')
115+
const s = new Readable()
116+
s.push(value)
117+
s.push(null)
118+
119+
const manifestHash = await bzz.upload(s, { size: value.length })
120+
121+
const data: Array<Uint8Array> = []
122+
const responseStream = await bzz.downloadStream(manifestHash, {
123+
mode: 'raw',
124+
})
125+
responseStream.on('data', (d: Uint8Array) => {
126+
data.push(d)
127+
})
128+
129+
return new Promise(resolve => {
130+
responseStream.on('end', () => {
131+
expect(Buffer.concat(data).toString()).toBe(value)
132+
resolve()
133+
})
134+
})
135+
})
136+
87137
it('downloading the manifest', async () => {
88138
const manifestHash = await bzz.upload(uploadContent, {
89139
contentType: 'text/plain',

__tests__/browser.ts

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,34 @@ describe('browser', () => {
1616
page.on('console', msg => {
1717
for (let i = 0; i < msg.args().length; ++i) {
1818
/* eslint-disable-next-line no-console */
19-
console.log(`${i}: ${msg.args()[i]}`)
19+
console.log(msg.args()[i])
2020
}
2121
})
2222

23+
page.on('pageerror', function(err) {
24+
/* eslint-disable-next-line no-console */
25+
console.log('Page error: ' + err.toString())
26+
})
27+
28+
page.on('error', function(err) {
29+
/* eslint-disable-next-line no-console */
30+
console.log('Error: ' + err.toString())
31+
})
32+
2333
await page.addScriptTag({
2434
path: resolve(
2535
__dirname,
2636
'../packages/swarm-browser/dist/erebos.swarm.development.js',
2737
),
2838
})
39+
40+
await page.addScriptTag({
41+
path: resolve(
42+
__dirname,
43+
'../packages/swarm-browser/dist/readable-stream.js',
44+
),
45+
})
46+
2947
await page.addScriptTag({
3048
path: resolve(
3149
__dirname,
@@ -134,6 +152,39 @@ describe('browser', () => {
134152
expect(evalResponse).toBe(uploadContent)
135153
})
136154

155+
it('uploads/downloads the file with bzz using streams', async () => {
156+
const manifestHash = await evalClient(async (client, uploadContent) => {
157+
const s = new NodeStream.Readable()
158+
s.push(uploadContent)
159+
s.push(null)
160+
161+
return await client.bzz.upload(s, {
162+
contentType: 'text/plain',
163+
})
164+
}, uploadContent)
165+
166+
const evalResponse = await evalClient(async (client, manifestHash) => {
167+
const response = await client.bzz.downloadStream(manifestHash)
168+
return new Promise(resolve => {
169+
const data: Array<Uint8Array> = []
170+
171+
response.on('data', (d: Uint8Array) => {
172+
data.push(d)
173+
})
174+
175+
response.on('end', () => {
176+
resolve(data)
177+
})
178+
})
179+
}, manifestHash)
180+
181+
// Reconstruct Buffer
182+
const decodedResponse = evalResponse.map(b =>
183+
Buffer.from(Object.values(b)),
184+
)
185+
expect(Buffer.concat(decodedResponse).toString()).toBe(uploadContent)
186+
})
187+
137188
it('lists common prefixes for nested directories', async () => {
138189
const expectedCommonPrefixes = ['dir1/', 'dir2/']
139190
const dirs = {
@@ -227,6 +278,31 @@ describe('browser', () => {
227278
expect(directoryList).toEqual({ ...dir, '/': dir[defaultPath] })
228279
})
229280

281+
it('downloadDirectoryData() streams the same data provided to uploadDirectory()', async () => {
282+
const dir = {
283+
[`foo-${uploadContent}.txt`]: {
284+
data: `this is foo-${uploadContent}.txt`,
285+
},
286+
[`bar-${uploadContent}.txt`]: {
287+
data: `this is bar-${uploadContent}.txt`,
288+
},
289+
}
290+
291+
const downloadedDir = await evalClient(async (client, dir) => {
292+
const dirHash = await client.bzz.uploadDirectory(dir)
293+
const response = await client.bzz.downloadDirectoryData(dirHash)
294+
return Object.keys(response).reduce(
295+
(prev, current) => ({
296+
...prev,
297+
[current]: { data: response[current].data.toString('utf8') },
298+
}),
299+
{},
300+
)
301+
}, dir)
302+
303+
expect(downloadedDir).toEqual(dir)
304+
})
305+
230306
it('supports feeds posting and getting', async () => {
231307
jest.setTimeout(20000)
232308
const data = { test: uploadContent }

docs/api-bzz.md

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -407,13 +407,33 @@ The `download()` method returns a [`Response` instance](https://developer.mozill
407407

408408
**Returns** `Promise<Response>`
409409

410+
### .downloadStream()
411+
412+
The `downloadStream()` method returns a NodeJs's compatible [`Readable` instance](https://nodejs.org/api/stream.html#stream_class_stream_readable) if the request succeeds, or throws a `HTTPError`.
413+
414+
**Arguments**
415+
416+
1. `hashOrDomain: string`: ENS name or Swarm hash
417+
1. [`options?: DownloadOptions = {}`](#downloadoptions) optional object providing the `path`, `mode` and `contentType`.
418+
419+
**Returns** `Promise<Readable>`
420+
421+
### .downloadDirectoryData()
422+
423+
**Arguments**
424+
425+
1. `hashOrDomain: string`: ENS name or Swarm hash
426+
1. [`options?: DownloadOptions = {}`](#downloadoptions)
427+
428+
**Returns** `Promise<DirectoryData>`
429+
410430
### .uploadFile()
411431

412432
Uploads a single file and returns the hash. If the `contentType` option is provided, it will return the manifest hash, otherwise the file will be uploaded as raw data and will return the hash of the data itself.
413433

414434
**Arguments**
415435

416-
1. `data: string | Buffer`
436+
1. `data: string | Buffer | Readable`
417437
1. [`options: UploadOptions = {}`](#uploadoptions)
418438

419439
**Returns** `Promise<string>`
@@ -667,15 +687,6 @@ Returns a [RxJS `Observable`](https://rxjs.dev/api/index/class/Observable) emitt
667687

668688
**Returns** `Observable<FileEntry>`
669689

670-
### .downloadDirectoryData()
671-
672-
**Arguments**
673-
674-
1. `hashOrDomain: string`: ENS name or Swarm hash
675-
1. [`options?: DownloadOptions = {}`](#downloadoptions)
676-
677-
**Returns** `Promise<DirectoryData>`
678-
679690
### .downloadTarTo()
680691

681692
**Arguments**
@@ -718,15 +729,6 @@ Call `downloadFileTo()` or `downloadDirectoryTo()` depending on the provided `pa
718729

719730
**Returns** `Promise<void>`
720731

721-
### .uploadFileStream()
722-
723-
**Arguments**
724-
725-
1. `stream: Readable`: Node.js [`Readable stream`](https://nodejs.org/dist/latest-v10.x/docs/api/stream.html#stream_class_stream_readable) instance
726-
1. [`options?: UploadOptions = {}`](#uploadoptions)
727-
728-
**Returns** `Promise<string>`
729-
730732
### .uploadTar()
731733

732734
**Arguments**

packages/api-bzz-base/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
"@babel/runtime": "^7.6.2",
2828
"@erebos/hex": "^0.10.0",
2929
"@erebos/keccak256": "^0.10.0",
30-
"rxjs": "^6.5.3"
30+
"readable-stream": "^3.1.1",
31+
"rxjs": "^6.5.3",
32+
"tar-stream": "^2.1.0"
33+
},
34+
"devDependencies": {
35+
"@types/readable-stream": "^2.3.5"
3136
}
3237
}

0 commit comments

Comments
 (0)