diff --git a/README.md b/README.md index 1aea54de..87ec3f6e 100644 --- a/README.md +++ b/README.md @@ -224,6 +224,7 @@ Run 'swarm-cli GROUP --help' to see available commands in a group █ Available Commands: +reupload Reupload and restamp content on the network upload Upload file to Swarm download Download arbitrary Swarm hash hash Print the Swarm hash of a file diff --git a/src/command/pinning/index.ts b/src/command/pinning/index.ts index 4fda72be..76911edf 100644 --- a/src/command/pinning/index.ts +++ b/src/command/pinning/index.ts @@ -1,7 +1,6 @@ import { GroupCommand } from 'furious-commander' import { List } from './list' import { Pin } from './pin' -import { Reupload } from './reupload' import { ReuploadAll } from './reupload-all' import { Unpin } from './unpin' @@ -10,5 +9,5 @@ export class Pinning implements GroupCommand { public readonly description = 'Pin, unpin and check pinned chunks' - public subCommandClasses = [Pin, Unpin, List, Reupload, ReuploadAll] + public subCommandClasses = [Pin, Unpin, List, ReuploadAll] } diff --git a/src/command/pinning/reupload.ts b/src/command/pinning/reupload.ts deleted file mode 100644 index 2cb25b00..00000000 --- a/src/command/pinning/reupload.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Argument, LeafCommand, Option } from 'furious-commander' -import { pickStamp } from '../../service/stamp' -import { stampProperties } from '../../utils/option' -import { PinningCommand } from './pinning-command' - -export class Reupload extends PinningCommand implements LeafCommand { - // CLI FIELDS - - public readonly name = 'reupload' - - public readonly description = 'Reupload locally pinned content' - - @Argument({ - key: 'address', - type: 'hex-string', - description: 'Pin address, reference of the collection or feed', - required: true, - length: 64, - }) - public address!: string - - @Option(stampProperties) - public stamp!: string - - public async run(): Promise { - await super.init() - - if (!this.stamp) { - this.stamp = await pickStamp(this.bee, this.console) - } - - this.console.log('Reuploading ' + this.address + '...') - try { - await this.bee.reuploadPinnedData(this.stamp, this.address) - this.console.log('Reuploaded successfully.') - } catch (error) { - this.console.printBeeError(error, { notFoundMessage: 'No locally pinned content found with that address.' }) - } - } -} diff --git a/src/command/reupload.ts b/src/command/reupload.ts new file mode 100644 index 00000000..648131b7 --- /dev/null +++ b/src/command/reupload.ts @@ -0,0 +1,52 @@ +import { Argument, LeafCommand, Option } from 'furious-commander' +import { pickStamp } from '../service/stamp' +import { stampProperties } from '../utils/option' +import { RootCommand } from './root-command' +import { History } from '../service/history' + +export class Reupload extends RootCommand implements LeafCommand { + // CLI FIELDS + + public readonly name = 'reupload' + + public readonly description = 'Reupload and restamp content on the network' + + @Argument({ + key: 'address', + type: 'hex-string', + description: 'Pin address, reference of the collection or feed', + required: true, + length: 64, + }) + public address!: string + + @Option(stampProperties) + public stamp!: string + + public async run(): Promise { + await super.init() + + if (!this.stamp) { + this.stamp = await pickStamp(this.bee, this.console) + } + + this.console.log('Reuploading ' + this.address + '...') + try { + await this.bee.reuploadPinnedData(this.stamp, this.address) + this.console.log('Reuploaded successfully.') + + if (this.commandConfig.config.historyEnabled) { + const history = new History(this.commandConfig, this.console) + history.addItem({ + timestamp: Date.now(), + reference: this.address, + stamp: this.stamp, + path: null, + uploadType: 'reupload', + }) + } + } catch (error) { + this.console.printBeeError(error, { notFoundMessage: 'No content found with that address.' }) + } + } +} diff --git a/src/config.ts b/src/config.ts index 05099e01..036af1fd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -18,6 +18,7 @@ import { Status } from './command/status' import { Upload } from './command/upload' import { Utility } from './command/utility' import { Wallet } from './command/wallet' +import { Reupload } from './command/reupload' export const beeApiUrl: IOption = { key: 'bee-api-url', @@ -127,4 +128,5 @@ export const rootCommandClasses = [ Quickstart, History, Access, + Reupload, ] diff --git a/src/service/history/types/history-item.ts b/src/service/history/types/history-item.ts index be6b9577..8f9253a4 100644 --- a/src/service/history/types/history-item.ts +++ b/src/service/history/types/history-item.ts @@ -4,7 +4,7 @@ export type HistoryItem = { reference: string stamp: string path: string | null - uploadType: 'file' | 'folder' | 'stdin' + uploadType: 'file' | 'folder' | 'stdin' | 'reupload' feedAddress?: string | null feedIdentity?: string | null } diff --git a/test/command/reupload.spec.ts b/test/command/reupload.spec.ts new file mode 100644 index 00000000..61dbf213 --- /dev/null +++ b/test/command/reupload.spec.ts @@ -0,0 +1,63 @@ +import { readFileSync } from 'fs' +import type { Reupload } from '../../src/command/reupload' +import type { Upload } from '../../src/command/upload' +import { HistoryItem } from '../../src/service/history/types/history-item' +import { describeCommand, invokeTestCli } from '../utility' +import { getStampOption } from '../utility/stamp' +import { Buy } from '../../src/command/stamp/buy' + +describeCommand( + 'Test Reupload command', + ({ consoleMessages, hasMessageContaining }) => { + it('should reupload pinned content and add it to the upload history', async () => { + // 1. Upload with --pin so there is locally pinned content + const uploadBuilder = await invokeTestCli([ + 'upload', + `${__dirname}/../testpage/images/swarm.png`, + '--pin', + ...getStampOption(), + ]) + const uploadCommand = uploadBuilder.runnable as Upload + const reference = uploadCommand.result.getOrThrow().toHex() + + // assert the upload itself succeeded before moving on + expect(uploadCommand.name).toBe('upload') + expect(reference).toHaveLength(64) + expect(hasMessageContaining('Swarm hash')).toBeTruthy() + + consoleMessages.length = 0 // clear messages from the upload + + // 2. buy a NEW stamp for the reupload + const buyExecution = await invokeTestCli([ + 'stamp', + 'buy', + '--depth', + '20', + '--amount', + '555m', + '--wait-usable', + '--yes', + ]) + const newStampId = (buyExecution.runnable as Buy).postageBatchId.toHex() + expect(newStampId).not.toBe(getStampOption()[1]) + + consoleMessages.length = 0 + + // 3. Reupload it + const reuploadBuilder = await invokeTestCli(['reupload', reference, '--stamp', newStampId]) + expect(hasMessageContaining('Reuploaded successfully')).toBeTruthy() + + // 4. Verify the history entry + const reuploadCommand = reuploadBuilder.runnable as Reupload + const historyPath = (reuploadCommand as any).commandConfig.getHistoryFilePath() + const items = JSON.parse(readFileSync(historyPath, 'utf-8')) as HistoryItem[] + + const lastItem = items[items.length - 1] + expect(lastItem.reference).toBe(reference) + expect(lastItem.uploadType).toBe('reupload') + expect(lastItem.path).toBeNull() + expect(lastItem.stamp).toBe(newStampId) + }) + }, + { configFileName: 'reupload' }, +)