diff --git a/src/pin/index.ts b/src/pin/index.ts index 2f9ed329f..292bca874 100644 --- a/src/pin/index.ts +++ b/src/pin/index.ts @@ -23,6 +23,11 @@ export interface PinAddOptions extends HTTPRPCOptions { * Internal option used to control whether to create a repo write lock during a pinning operation */ lock?: boolean + + /** + * An optional name for created pin(s) + */ + name?: string } export interface PinAddAllOptions extends HTTPRPCOptions { @@ -68,12 +73,14 @@ export type PinQueryType = 'recursive' | 'direct' | 'indirect' | 'all' export interface PinLsOptions extends HTTPRPCOptions { paths?: CID | CID[] | string | string[] type?: PinQueryType + name?: string } export interface PinLsResult { cid: CID type: PinType | string metadata?: Record + name?: string } export interface PinRmOptions extends HTTPRPCOptions { diff --git a/src/pin/ls.ts b/src/pin/ls.ts index 1483efc25..234413499 100644 --- a/src/pin/ls.ts +++ b/src/pin/ls.ts @@ -3,7 +3,7 @@ import { toUrlSearchParams } from '../lib/to-url-search-params.js' import type { PinAPI, PinLsResult } from './index.js' import type { HTTPRPCClient } from '../lib/core.js' -function toPin (type: string, cid: string, metadata: Record): PinLsResult { +function toPin (type: string, cid: string, metadata: Record, name: string): PinLsResult { const pin: PinLsResult = { type, cid: CID.parse(cid) @@ -12,6 +12,9 @@ function toPin (type: string, cid: string, metadata: Record): Pi if (metadata != null) { pin.metadata = metadata } + if (name != null) { + pin.name = name + } return pin } @@ -37,12 +40,12 @@ export function createLs (client: HTTPRPCClient): PinAPI['ls'] { for await (const pin of res.ndjson()) { if (pin.Keys != null) { // non-streaming response for (const cid of Object.keys(pin.Keys)) { - yield toPin(pin.Keys[cid].Type, cid, pin.Keys[cid].Metadata) + yield toPin(pin.Keys[cid].Type, cid, pin.Keys[cid].Metadata, pin.Keys[cid].Name) } return } - yield toPin(pin.Type, pin.Cid, pin.Metadata) + yield toPin(pin.Type, pin.Cid, pin.Metadata, pin.Name) } } } diff --git a/test/interface-tests/src/pin/ls.ts b/test/interface-tests/src/pin/ls.ts index ce650d76b..fd4ceb77e 100644 --- a/test/interface-tests/src/pin/ls.ts +++ b/test/interface-tests/src/pin/ls.ts @@ -24,10 +24,10 @@ export function testLs (factory: Factory, options: MochaConfig): void await ipfs.pin.add(fixtures.directory.cid, { recursive: true }) // a file (CID pinned recursively) await ipfs.add(fixtures.files[0].data, { pin: false, cidVersion: 0 }) - await ipfs.pin.add(fixtures.files[0].cid, { recursive: true }) + await ipfs.pin.add(fixtures.files[0].cid, { recursive: true, name: fixtures.files[0].pinName }) // a single CID (pinned directly) await ipfs.add(fixtures.files[1].data, { pin: false, cidVersion: 0 }) - await ipfs.pin.add(fixtures.files[1].cid, { recursive: false }) + await ipfs.pin.add(fixtures.files[1].cid, { recursive: false, name: fixtures.files[1].pinName }) }) after(async function () { @@ -77,6 +77,7 @@ export function testLs (factory: Factory, options: MochaConfig): void const pinset = await all(ipfs.pin.ls()) expect(pinset).to.not.be.empty() + // check the three "roots" expect(pinset).to.deep.include({ type: 'recursive', @@ -98,6 +99,11 @@ export function testLs (factory: Factory, options: MochaConfig): void type: 'indirect', cid: fixtures.directory.files[1].cid }) + + // Verify each pin has no name property + for (const pin of pinset) { + expect(pin).to.not.have.property('name') + } }) it('should list all direct pins', async () => { @@ -174,6 +180,30 @@ export function testLs (factory: Factory, options: MochaConfig): void expect(cids).to.include(fixtures.files[1].cid.toString()) }) + it('should list multiple partially matched named pins', async () => { + const pinset = await all(ipfs.pin.ls({ + name: 'file' + })) + expect(pinset).to.have.lengthOf(2) + const cids = pinset.map(p => p.cid.toString()) + expect(cids).to.include(fixtures.files[0].cid.toString()) + expect(cids).to.include(fixtures.files[1].cid.toString()) + const names = pinset.map(p => p.name) + expect(names).to.include(fixtures.files[0].pinName) + expect(names).to.include(fixtures.files[1].pinName) + }) + + it('should list specific named pin', async () => { + const pinset = await all(ipfs.pin.ls({ + name: fixtures.files[0].pinName + })) + expect(pinset).to.have.lengthOf(1) + const cids = pinset.map(p => p.cid.toString()) + expect(cids).to.include(fixtures.files[0].cid.toString()) + const names = pinset.map(p => p.name) + expect(names).to.include(fixtures.files[0].pinName) + }) + it('should throw error for invalid non-string pin type option', () => { // @ts-expect-error wrong pin type return expect(all(ipfs.pin.ls({ type: 6 }))) diff --git a/test/interface-tests/src/pin/utils.ts b/test/interface-tests/src/pin/utils.ts index 1a75e02fb..36ddfa51a 100644 --- a/test/interface-tests/src/pin/utils.ts +++ b/test/interface-tests/src/pin/utils.ts @@ -36,10 +36,12 @@ export const fixtures = Object.freeze({ }), files: Object.freeze([Object.freeze({ data: uint8ArrayFromString('Plz add me!\n'), - cid: CID.parse('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP') + cid: CID.parse('Qma4hjFTnCasJ8PVp3mZbZK5g2vGDT4LByLJ7m8ciyRFZP'), + pinName: 'file-1' }), Object.freeze({ data: loadFixture('test/interface-tests/fixtures/test-folder/files/hello.txt'), - cid: CID.parse('QmY9cxiHqTFoWamkQVkpmmqzBrY3hCBEL2XNu3NtX74Fuu') + cid: CID.parse('QmY9cxiHqTFoWamkQVkpmmqzBrY3hCBEL2XNu3NtX74Fuu'), + pinName: 'file-2' })]) })