diff --git a/package.json b/package.json index a1d9591a..31e56c46 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "typescript": "^4.8.4" }, "dependencies": { - "@ethersphere/bee-js": "^6.7.3", + "@ethersphere/bee-js": "^6.9.1", "@fairdatasociety/bmt-js": "^2.1.0", "bignumber.js": "^9.1.0", "chalk": "^2.4.2", diff --git a/src/command/feed/feed-command.ts b/src/command/feed/feed-command.ts index a3fc4d30..f035941e 100644 --- a/src/command/feed/feed-command.ts +++ b/src/command/feed/feed-command.ts @@ -38,10 +38,13 @@ export class FeedCommand extends RootCommand { @Option({ key: 'password', alias: 'P', description: 'Password for the wallet' }) public password!: string + @Option({ key: 'index', description: 'Feed index to write to or read from', required: false }) + public index!: number | undefined + protected async updateFeedAndPrint(chunkReference: string): Promise { const wallet = await this.getWallet() const topic = this.topic || this.bee.makeFeedTopic(this.topicString) - const { reference, manifest } = await this.writeFeed(wallet, topic, chunkReference) + const { reference, manifest } = await this.writeFeed(wallet, topic, chunkReference, this.index) this.console.verbose(createKeyValue('Chunk Reference', chunkReference)) this.console.verbose(createKeyValue('Chunk Reference URL', `${this.bee.url}/bzz/${chunkReference}/`)) @@ -79,16 +82,23 @@ export class FeedCommand extends RootCommand { return identities[this.identity] || identities[await pickIdentity(this.commandConfig, this.console)] } - private async writeFeed(wallet: Wallet, topic: string, chunkReference: string): Promise { + private async writeFeed(wallet: Wallet, topic: string, chunkReference: string, index?: number): Promise { const spinner = createSpinner('Writing feed...') if (this.verbosity !== VerbosityLevel.Quiet && !this.curl) { spinner.start() } - + try { const writer = this.bee.makeFeedWriter('sequence', topic, wallet.getPrivateKey()) - const reference = await writer.upload(this.stamp, chunkReference as Reference) + let reference: Reference + if (index === undefined) { + // Index was not specified + reference = await writer.upload(this.stamp, chunkReference as Reference) + } else { + // Index was specified + reference = await writer.upload(this.stamp, chunkReference as Reference, { index: Number(index) }) + } const { reference: manifest } = await this.bee.createFeedManifest( this.stamp, 'sequence', diff --git a/src/command/feed/print.ts b/src/command/feed/print.ts index 15bda9d3..03399fde 100644 --- a/src/command/feed/print.ts +++ b/src/command/feed/print.ts @@ -8,6 +8,7 @@ import { getFieldOrNull } from '../../utils' import { createSpinner } from '../../utils/spinner' import { createKeyValue } from '../../utils/text' import { FeedCommand } from './feed-command' +import { FetchFeedUpdateResponse } from '@ethersphere/bee-js/dist/types/modules/feed' export class Print extends FeedCommand implements LeafCommand { public readonly name = 'print' @@ -28,12 +29,24 @@ export class Print extends FeedCommand implements LeafCommand { await super.init() const topic = this.topic || this.bee.makeFeedTopic(this.topicString) + const index: number | undefined = this.index const spinner = createSpinner(`Looking up feed topic ${topic}`) spinner.start() try { const addressString = this.address || (await this.getAddressString()) const reader = this.bee.makeFeedReader('sequence', topic, addressString) - const { reference, feedIndex, feedIndexNext } = await reader.download() + + let result: FetchFeedUpdateResponse | null = null; + if (index === undefined) { + // Index was not specified + result = await reader.download() + } else { + // Index was specified + // typeof index is string, and we don't understand why. This is why we are doing this conversion. + result = await reader.download({ index: Number(index) }) + } + if (!result) throw Error('Error downloading feed update') + const { reference, feedIndex, feedIndexNext } = result; if (!this.stamp) { spinner.stop() diff --git a/src/command/feed/update.ts b/src/command/feed/update.ts index 5d9af4ae..b21ea382 100644 --- a/src/command/feed/update.ts +++ b/src/command/feed/update.ts @@ -5,7 +5,7 @@ import { FeedCommand } from './feed-command' export class Update extends FeedCommand implements LeafCommand { public readonly name = 'update' - public readonly description = 'Update feed' + public readonly description = 'Update feed (with reference)' @Option({ key: 'reference', alias: 'r', description: 'The new reference', required: true }) public reference!: string diff --git a/src/command/feed/upload.ts b/src/command/feed/upload.ts index c3548d22..e5ac6315 100644 --- a/src/command/feed/upload.ts +++ b/src/command/feed/upload.ts @@ -6,7 +6,7 @@ import { FeedCommand } from './feed-command' export class Upload extends FeedCommand implements LeafCommand { public readonly name = 'upload' - public readonly description = 'Upload to a feed' + public readonly description = 'Upload to a feed (file or folder)' public feedManifest?: string diff --git a/test/command/feed.spec.ts b/test/command/feed.spec.ts index baa50364..52b1bcc4 100644 --- a/test/command/feed.spec.ts +++ b/test/command/feed.spec.ts @@ -110,6 +110,145 @@ describeCommand( expect(getLastMessage()).toContain('Number of Updates') expect(getLastMessage()).toContain('2') }) + + + it('upload should write to correct index', async () => { + const identityName = 'test' + const topicName = 'test' + const password = 'test' + // create identity + await invokeTestCli(['identity', 'create', identityName, '--password', 'test']) + // upload data to index 22 + await invokeTestCli([ + 'feed', + 'upload', + `${__dirname}/../testpage/images/swarm.png`, + '--identity', + identityName, + '--topic-string', + topicName, + '--password', + password, + '--quiet', + '--index', + '22', + ...getStampOption(), + ]) + // print with identity and password + await invokeTestCli([ + 'feed', + 'print', + '--identity', + identityName, + '--topic-string', + topicName, + '--password', + password, + '--quiet', + '--index', + '22', + ...getStampOption(), + ]) + expect(getLastMessage()).toMatch(/[a-z0-9]{64}/) + + // Zero index should work as well + await invokeTestCli([ + 'feed', + 'upload', + `${__dirname}/../testpage/images/swarm.png`, + '--identity', + identityName, + '--topic-string', + topicName, + '--password', + password, + '--quiet', + '--index', + '0', + ...getStampOption(), + ]) + await invokeTestCli([ + 'feed', + 'print', + '--identity', + identityName, + '--topic-string', + topicName, + '--password', + password, + '--quiet', + '--index', + '0', + ...getStampOption(), + ]) + expect(getLastMessage()).toMatch(/[a-z0-9]{64}/) + + // It should work without specifying the index as well + await invokeTestCli([ + 'feed', + 'print', + '--identity', + identityName, + '--topic-string', + topicName, + '--password', + password, + '--quiet', + ...getStampOption(), + ]) + expect(getLastMessage()).toMatch(/[a-z0-9]{64}/) + }) + + it('update should write to correct index', async () => { + const identityName = 'test' + const topicName = 'test' + const password = 'test' + // create identity + await invokeTestCli(['identity', 'create', 'test', '--password', 'test']) + // upload data and get reference + await invokeTestCli([ + 'upload', + `${__dirname}/../testpage/images/swarm.png`, + '--quiet', + ...getStampOption(), + ]) + const reference = getLastMessage(); + // update the feed with newly got reference + await invokeTestCli([ + 'feed', + 'update', + '--reference', + reference, + '--identity', + identityName, + '--topic-string', + topicName, + '--password', + password, + '--quiet', + '--index', + '22', + ...getStampOption(), + ]) + + // print with identity and password + await invokeTestCli([ + 'feed', + 'print', + '--identity', + 'test', + '--topic-string', + 'test', + '--password', + 'test', + '--quiet', + '--index', + '22', + ...getStampOption(), + ]) + expect(getLastMessage()).toMatch(/[a-z0-9]{64}/) + }) + }, { configFileName: 'feed' }, )