diff --git a/packages/client/.aegir.js b/packages/client/.aegir.js index 1aeb139..dfbc076 100644 --- a/packages/client/.aegir.js +++ b/packages/client/.aegir.js @@ -23,13 +23,11 @@ const options = { callCount++ try { - let data - try { - // when passed data from a test where `body=providers.map(prov => JSON.stringify(prov)).join('\n')` - data = { Providers: req.body.split('\n').map(line => JSON.parse(line)) } - } catch (err) { - // when passed data from a test where `body=JSON.stringify({ Providers: providers })` - data = req.body + // if request body content-type was json it's already been parsed + const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body + + if (!Array.isArray(data.Providers)) { + throw new Error('Data must be { Providers: [] }') } providers.set(req.params.cid, data) @@ -50,6 +48,12 @@ const options = { const providerData = providers.get(req.params.cid) || { Providers: [] } const acceptHeader = req.headers.accept + if (providerData?.Providers?.length === 0) { + res.statusCode = 404 + res.end() + return + } + if (acceptHeader?.includes('application/x-ndjson')) { res.setHeader('Content-Type', 'application/x-ndjson') const providers = Array.isArray(providerData.Providers) ? providerData.Providers : [] diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 763a186..2f6ff14 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -118,7 +118,8 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV await onStart.promise // https://specs.ipfs.tech/routing/http-routing-v1/ - const url = new URL(`${this.clientUrl}routing/v1/providers/${cid.toString()}`) + const url = new URL(`${this.clientUrl}routing/v1/providers/${cid}`) + this.#addFilterParams(url, options.filterAddrs, options.filterProtocols) const getOptions = { headers: { Accept: 'application/x-ndjson' }, signal } const res = await this.#makeRequest(url.toString(), getOptions) @@ -126,6 +127,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV if (res == null) { throw new BadResponseError('No response received') } + if (!res.ok) { if (res.status === 404) { // https://specs.ipfs.tech/routing/http-routing-v1/#response-status-codes diff --git a/packages/client/src/routings.ts b/packages/client/src/routings.ts index b80540f..a6658c6 100644 --- a/packages/client/src/routings.ts +++ b/packages/client/src/routings.ts @@ -25,12 +25,22 @@ export class DelegatedRoutingV1HttpApiClientContentRouting implements ContentRou } async * findProviders (cid: CID, options: AbortOptions = {}): AsyncIterable { - yield * map(this.client.getProviders(cid, options), (record) => { - return { - id: record.ID, - multiaddrs: record.Addrs ?? [] + try { + yield * map(this.client.getProviders(cid, options), (record) => { + return { + id: record.ID, + multiaddrs: record.Addrs ?? [] + } + }) + } catch (err) { + // NotFoundError means no providers were found so end the iterator instead + // of throwing which means there was an error + if (err instanceof NotFoundError) { + return } - }) + + throw err + } } async provide (): Promise { diff --git a/packages/client/test/index.spec.ts b/packages/client/test/index.spec.ts index 7e6a73c..f77ff96 100644 --- a/packages/client/test/index.spec.ts +++ b/packages/client/test/index.spec.ts @@ -51,9 +51,11 @@ describe('delegated-routing-v1-http-api-client', () => { const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') // load providers for the router to fetch - await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, { + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { method: 'POST', - body: providers.map(prov => JSON.stringify(prov)).join('\n') + body: JSON.stringify({ + Providers: providers + }) }) const provs = await all(client.getProviders(cid)) @@ -84,12 +86,14 @@ describe('delegated-routing-v1-http-api-client', () => { for (const contentType of contentTypes) { // Add providers with proper payload structure - await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, { + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { method: 'POST', headers: { 'Content-Type': contentType }, - body: JSON.stringify({ Providers: providers }) + body: JSON.stringify({ + Providers: providers + }) }) await new Promise((resolve) => setTimeout(resolve, 100)) @@ -122,9 +126,11 @@ describe('delegated-routing-v1-http-api-client', () => { const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') // load providers for the router to fetch - await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, { + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { method: 'POST', - body: providers.map(prov => JSON.stringify(prov)).join('\n') + body: JSON.stringify({ + Providers: providers + }) }) await all(client.getProviders(cid, { filterProtocols: ['transport-bitswap', 'unknown'], filterAddrs: ['webtransport', '!p2p-circuit'] })) @@ -162,9 +168,13 @@ describe('delegated-routing-v1-http-api-client', () => { const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') // load providers for the router to fetch - await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, { + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { method: 'POST', - body: 'not json' + body: JSON.stringify({ + Providers: [ + 'not json' + ] + }) }) const provs = await all(client.getProviders(cid)) @@ -188,9 +198,11 @@ describe('delegated-routing-v1-http-api-client', () => { const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') // load providers for the router to fetch - await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, { + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { method: 'POST', - body: providers.map(prov => JSON.stringify(prov)).join('\n') + body: JSON.stringify({ + Providers: providers + }) }) const provs = await all(client.getProviders(cid)) @@ -351,9 +363,11 @@ describe('delegated-routing-v1-http-api-client', () => { }] // load providers for the router to fetch - await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, { + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { method: 'POST', - body: providers.map(prov => JSON.stringify(prov)).join('\n') + body: JSON.stringify({ + Providers: providers + }) }) // Reset call count before our test @@ -404,9 +418,11 @@ describe('delegated-routing-v1-http-api-client', () => { }] // load providers for the router to fetch - await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, { + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { method: 'POST', - body: providers.map(prov => JSON.stringify(prov)).join('\n') + body: JSON.stringify({ + Providers: providers + }) }) // Reset call count diff --git a/packages/client/test/routings.spec.ts b/packages/client/test/routings.spec.ts index 053681d..ec6a936 100644 --- a/packages/client/test/routings.spec.ts +++ b/packages/client/test/routings.spec.ts @@ -67,9 +67,11 @@ describe('libp2p content-routing', () => { const cid = CID.parse('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') // load providers for the router to fetch - await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, { + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { method: 'POST', - body: providers.map(prov => JSON.stringify(prov)).join('\n') + body: JSON.stringify({ + Providers: providers + }) }) const provs = await all(routing.findProviders(cid)) @@ -82,6 +84,26 @@ describe('libp2p content-routing', () => { }))) }) + it('should yield no results if no providers exist', async () => { + const routing = getContentRouting(client) + + if (routing == null) { + throw new Error('ContentRouting not found') + } + + const cid = CID.parse('QmawceGscqN4o8Y8Fv26UUmB454kn2bnkXV5tEQYc4jBd7') + + // load providers for the router to fetch + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { + method: 'POST', + body: JSON.stringify({ + Providers: [] + }) + }) + + await expect(all(routing.findProviders(cid))).to.eventually.have.lengthOf(0) + }) + it('should respect abort signal when finding providers', async () => { const routing = getContentRouting(client) @@ -97,9 +119,11 @@ describe('libp2p content-routing', () => { const cid = CID.parse('QmawceGscqN4o8Y8Fv26UUmB454kn2bnkXV5tEQYc4jBd6') // load providers for the router to fetch - await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid.toString()}`, { + await fetch(`${process.env.ECHO_SERVER}/add-providers/${cid}`, { method: 'POST', - body: providers.map(prov => JSON.stringify(prov)).join('\n') + body: JSON.stringify({ + Providers: providers + }) }) let findProvidersFinished = false diff --git a/packages/interop/package.json b/packages/interop/package.json index ed6a5fb..b545954 100644 --- a/packages/interop/package.json +++ b/packages/interop/package.json @@ -20,8 +20,8 @@ "lint": "aegir lint", "dep-check": "aegir dep-check", "build": "aegir build --bundle false", - "test": "aegir test -t node", - "test:node": "aegir test -t node --cov" + "test": "aegir test -t node -- --exit", + "test:node": "aegir test -t node --cov -- --exit" }, "devDependencies": { "@helia/delegated-routing-v1-http-api-client": "^5.0.0", diff --git a/packages/interop/test/index.spec.ts b/packages/interop/test/index.spec.ts index 43de38a..760714b 100644 --- a/packages/interop/test/index.spec.ts +++ b/packages/interop/test/index.spec.ts @@ -50,13 +50,9 @@ describe('delegated-routing-v1-http-api interop', () => { }) afterEach(async () => { - await stop(client) - - if (server != null) { - await server.close() - } - await Promise.all(network.map(async node => node.stop())) + await server?.close() + await stop(client) }) it('should find providers', async () => { @@ -98,7 +94,7 @@ describe('delegated-routing-v1-http-api interop', () => { // use client to resolve the published record const record = await client.getIPNS(result.publicKey.toCID()) - expect(record.value).to.equal(`/ipfs/${cid.toString()}`) + expect(record.value).to.equal(`/ipfs/${cid}`) }) it.skip('should put an IPNS record', async () => {