Skip to content

Commit

Permalink
implement can store ls and can upload ls (#45)
Browse files Browse the repository at this point in the history
Implement `can store ls` and `can upload ls` commands, including support
for the `pre` caveat introduced in
storacha/w3up#423 and support for the
`size` and `cursor` caveats, set with options of the same name.

Also make some docs changes for more consistency and less speling erors.
  • Loading branch information
travis authored Mar 2, 2023
1 parent a6f3b8d commit 4ad8f4c
Show file tree
Hide file tree
Showing 10 changed files with 491 additions and 500 deletions.
39 changes: 27 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ w3 up recipies.txt
* [`w3 can space info`](#w3-can-space-info-did) <sup>coming soon!</sup>
* [`w3 can space recover`](#w3-can-space-recover-email) <sup>coming soon!</sup>
* [`w3 can store add`](#w3-can-store-add-car-path)
* [`w3 can store ls`](#w3-can-store-ls) <sup>coming soon!</sup>
* [`w3 can store ls`](#w3-can-store-ls)
* [`w3 can store rm`](#w3-can-store-rm-car-cid) <sup>coming soon!</sup>
* [`w3 can upload add`](#w3-can-upload-add-root-cid-shard-cid-shard-cid)
* [`w3 can upload ls`](#w3-can-upload-ls) <sup>coming soon!</sup>
* [`w3 can upload ls`](#w3-can-upload-ls)
* [`w3 can upload rm`](#w3-can-upload-rm-root-cid) <sup>coming soon!</sup>

---
Expand All @@ -68,24 +68,24 @@ w3 up recipies.txt

Upload file(s) to web3.storage. The IPFS Content ID (CID) for your files is calculated on your machine, and sent up along with your files. web3.storage makes your content available on the IPFS network

- `--no-wrap` Don't wrap input files with a directory.
- `-H, --hidden` Include paths that start with ".".
- `-c, --car` File is a CAR file.
- `--shard-size` Shard uploads into CAR files of approximately this size in bytes.
- `--concurrent-requests` Send up to this many CAR shards concurrently.
* `--no-wrap` Don't wrap input files with a directory.
* `-H, --hidden` Include paths that start with ".".
* `-c, --car` File is a CAR file.
* `--shard-size` Shard uploads into CAR files of approximately this size in bytes.
* `--concurrent-requests` Send up to this many CAR shards concurrently.

### `w3 ls`

List all the uploads registered in the current space.

- `--json` Format as newline delimted JSON
- `--shards` Pretty print with shards in output
* `--json` Format as newline delimited JSON
* `--shards` Pretty print with shards in output

### `w3 rm <root-cid>`

Remove an upload from the uploads listing. Note that this command does not remove the data from the IPFS network, nor does it remove it from space storage (by default).

- `--shards` Also remove all shards referenced by the upload from the store. Use with caution and ensure other uploads do not reference the same shards.
* `--shards` Also remove all shards referenced by the upload from the store. Use with caution and ensure other uploads do not reference the same shards.

### `w3 open <cid>`

Expand Down Expand Up @@ -136,7 +136,7 @@ Create a delegation to the passed audience for the given abilities with the _cur

List delegations created by this agent for others.

- `--json` Format as newline delimted JSON
* `--json` Format as newline delimited JSON

### `w3 proof add <proof.ucan>`

Expand All @@ -146,7 +146,7 @@ Add a proof delegated to this agent. The proof is a CAR encoded delegation to _t

List proofs of delegated capabilities. Proofs are delegations with an audience matching the agent DID.

- `--json` Format as newline delimted JSON
* `--json` Format as newline delimited JSON

### `w3 can space info <did>`

Expand All @@ -158,6 +158,13 @@ Store a [CAR](https://ipld.io/specs/transport/car/carv1/) file to web3.storage.

### `w3 can store ls`

List CARs in the current space.

* `--json` Format as newline delimited JSON
* `--size` The desired number of results to return
* `--cursor` An opaque string included in a prior upload/list response that allows the service to provide the next "page" of results
* `--pre` If true, return the page of results preceding the cursor

### `w3 can store rm <car-cid>`

### `w3 can upload add <root-cid> <shard-cid> [shard-cid...]`
Expand All @@ -166,6 +173,14 @@ Register an upload - a DAG with the given root data CID that is stored in the gi

### `w3 can upload ls`

List uploads in the current space.

* `--json` Format as newline delimited JSON
* `--shards` Pretty print with shards in output
* `--size` The desired number of results to return
* `--cursor` An opaque string included in a prior upload/list response that allows the service to provide the next "page" of results
* `--pre` If true, return the page of results preceding the cursor

### `w3 can upload rm <root-cid>`

## Contributing
Expand Down
29 changes: 24 additions & 5 deletions bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import {
} from './index.js'
import {
storeAdd,
uploadAdd
storeList,
uploadAdd,
uploadList
} from './can.js'

const cli = sade('w3')
Expand All @@ -46,7 +48,7 @@ cli.command('open <cid>')
cli.command('ls')
.alias('list')
.describe('List uploads in the current space')
.option('--json', 'Format as newline delimted JSON')
.option('--json', 'Format as newline delimited JSON')
.option('--shards', 'Pretty print with shards in output')
.action(list)

Expand Down Expand Up @@ -94,26 +96,43 @@ cli.command('delegation create <audience-did>')

cli.command('delegation ls')
.describe('List delegations created by this agent for others.')
.option('--json', 'Format as newline delimted JSON')
.option('--json', 'Format as newline delimited JSON')
.action(listDelegations)

cli.command('proof add <proof>')
.describe('Add a proof delegated to this agent.')
.option('--json', 'Format as newline delimted JSON')
.option('--json', 'Format as newline delimited JSON')
.option('--dry-run', 'Decode and view the proof but do not add it')
.action(addProof)

cli.command('proof ls')
.describe('List proofs of capabilities delegated to this agent.')
.option('--json', 'Format as newline delimted JSON')
.option('--json', 'Format as newline delimited JSON')
.action(listProofs)

cli.command('can store add <car-path>')
.describe('Store a CAR file with the service.')
.action(storeAdd)

cli.command('can store ls')
.describe('List CAR files in the current space.')
.option('--json', 'Format as newline delimited JSON')
.option('--size', 'The desired number of results to return')
.option('--cursor', 'An opaque string included in a prior store/list response that allows the service to provide the next "page" of results')
.option('--pre', 'If true, return the page of results preceding the cursor')
.action(storeList)

cli.command('can upload add <root-cid> <shard-cid>')
.describe('Register an upload - a DAG with the given root data CID that is stored in the given CAR shard(s), identified by CAR CIDs.')
.action(uploadAdd)

cli.command('can upload ls')
.describe('List uploads in the current space.')
.option('--json', 'Format as newline delimited JSON')
.option('--shards', 'Pretty print with shards in output')
.option('--size', 'The desired number of results to return')
.option('--cursor', 'An opaque string included in a prior upload/list response that allows the service to provide the next "page" of results')
.option('--pre', 'If true, return the page of results preceding the cursor')
.action(uploadList)

cli.parse(process.argv)
57 changes: 56 additions & 1 deletion can.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import fs from 'fs'
import { CID } from 'multiformats'
import ora from 'ora'
import { getClient } from './lib.js'
import { getClient, uploadListResponseToString, storeListResponseToString } from './lib.js'

/**
* @param {string} carPath
Expand All @@ -27,6 +27,33 @@ export async function storeAdd (carPath) {
spinner.stopAndPersist({ symbol: '⁂', text: `Stored ${cid}` })
}

/**
* Print out all the CARs in the current space.
* @param {object} opts
* @param {boolean} [opts.json]
* @param {string} [opts.cursor]
* @param {number} [opts.size]
* @param {boolean} [opts.pre]
*/
export async function storeList (opts = {}) {
const client = await getClient()
const listOptions = {}
if (opts.size) {
listOptions.size = parseInt(opts.size)
}
if (opts.cursor) {
listOptions.cursor = opts.cursor
}
if (opts.pre) {
listOptions.pre = opts.pre
}

const spinner = ora('Listing CARs').start()
const res = await client.capability.store.list(listOptions)
spinner.stop()
console.log(storeListResponseToString(res, opts))
}

/**
* @param {string} root
* @param {string} shard
Expand Down Expand Up @@ -60,3 +87,31 @@ export async function uploadAdd (root, shard, opts) {
await client.capability.upload.add(rootCID, shards)
spinner.stopAndPersist({ symbol: '⁂', text: `Upload added ${rootCID}` })
}

/**
* Print out all the uploads in the current space.
* @param {object} opts
* @param {boolean} [opts.json]
* @param {boolean} [opts.shards]
* @param {string} [opts.cursor]
* @param {number} [opts.size]
* @param {boolean} [opts.pre]
*/
export async function uploadList (opts = {}) {
const client = await getClient()
const listOptions = {}
if (opts.size) {
listOptions.size = parseInt(opts.size)
}
if (opts.cursor) {
listOptions.cursor = opts.cursor
}
if (opts.pre) {
listOptions.pre = opts.pre
}

const spinner = ora('Listing uploads').start()
const res = await client.capability.upload.list(listOptions)
spinner.stop()
console.log(uploadListResponseToString(res, opts))
}
22 changes: 3 additions & 19 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import fs from 'fs'
import ora, { oraPromise } from 'ora'
import tree from 'pretty-tree'
import { Readable } from 'stream'
import { CID } from 'multiformats/cid'
import * as DID from '@ipld/dag-ucan/did'
import { CarWriter } from '@ipld/car'
import { getClient, checkPathsExist, filesize, readProof, filesFromPaths } from './lib.js'
import { getClient, checkPathsExist, filesize, readProof, filesFromPaths, uploadListResponseToString } from './lib.js'

/**
* @param {string} firstPath
Expand Down Expand Up @@ -55,30 +54,15 @@ export async function upload (firstPath, opts) {
* @param {boolean} [opts.json]
* @param {boolean} [opts.shards]
*/
export async function list (opts) {
export async function list (opts = {}) {
const client = await getClient()
let count = 0
let res
do {
res = await client.capability.upload.list()
count += res.results.length
if (res.results.length) {
if (opts.json) {
console.log(res.results.map(({ root, shards }) => JSON.stringify({
root: root.toString(),
shards: shards?.map(s => s.toString())
})).join('\n'))
} else if (opts.shards) {
console.log(res.results.map(({ root, shards }) => tree({
label: root.toString(),
nodes: [{
label: 'shards',
leaf: shards?.map(s => s.toString())
}]
})).join('\n'))
} else {
console.log(res.results.map(({ root }) => root.toString()).join('\n'))
}
console.log(uploadListResponseToString(res, opts))
}
} while (res.cursor && res.results.length)

Expand Down
48 changes: 48 additions & 0 deletions lib.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fs from 'fs'
import path from 'path'
import tree from 'pretty-tree'
import { Readable } from 'stream'
import { importDAG } from '@ucanto/core/delegation'
import { connect } from '@ucanto/client'
Expand All @@ -13,6 +14,9 @@ import { CarReader } from '@ipld/car'

/**
* @typedef {import('@web3-storage/w3up-client/types').FileLike & { size: number }} FileLike
* @typedef {import('@web3-storage/w3up-client/types').ListResponse} ListResponse
* @typedef {import('@web3-storage/w3up-client/types').StoreListResult} StoreListResult
* @typedef {import('@web3-storage/w3up-client/types').UploadListResult} UploadListResult
*/

export function getPkg () {
Expand Down Expand Up @@ -193,3 +197,47 @@ async function * filesFromDir (dir, filter) {
}
}
}

/**
* @param {ListResponse<UploadListResult>} res
* @param {boolean} [opts.raw]
* @param {boolean} [opts.json]
* @param {boolean} [opts.shards]
* @returns {string}
*/
export function uploadListResponseToString (res, opts = {}) {
if (opts.json) {
return res.results.map(({ root, shards }) => JSON.stringify({
root: root.toString(),
shards: shards?.map(s => s.toString())
})).join('\n')
} else if (opts.shards) {
return res.results.map(({ root, shards }) => tree({
label: root.toString(),
nodes: [{
label: 'shards',
leaf: shards?.map(s => s.toString())
}]
})).join('\n')
} else {
return res.results.map(({ root }) => root.toString()).join('\n')
}
}

/**
* @param {ListResponse<StoreListResult>} res
* @param {boolean} [opts.raw]
* @param {boolean} [opts.json]
* @returns {string}
*/
export function storeListResponseToString (res, opts = {}) {
if (opts.json) {
return res.results.map(({ link, size, insertedAt }) => JSON.stringify({
link: link.toString(),
size,
insertedAt
})).join('\n')
} else {
return res.results.map(({ link }) => link.toString()).join('\n')
}
}
Loading

0 comments on commit 4ad8f4c

Please sign in to comment.