Skip to content

Commit

Permalink
feat: support pagination (#204)
Browse files Browse the repository at this point in the history
Closes #201
  • Loading branch information
Alan Shaw authored Nov 23, 2022
1 parent 97ef185 commit a5296a6
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 48 deletions.
23 changes: 12 additions & 11 deletions packages/upload-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,21 @@
"project": "./tsconfig.json"
},
"rules": {
"unicorn/prefer-number-properties": "off",
"unicorn/no-null": "off",
"unicorn/prefer-set-has": "off",
"unicorn/no-array-for-each": "off",
"unicorn/prefer-export-from": "off",
"unicorn/catch-error-name": "off",
"unicorn/explicit-length-check": "off",
"unicorn/prefer-type-error": "off",
"eqeqeq": "off",
"no-void": "off",
"jsdoc/check-indentation": "off",
"jsdoc/require-hyphen-before-param-description": "off",
"no-console": "off",
"no-continue": "off",
"jsdoc/check-indentation": "off",
"jsdoc/require-hyphen-before-param-description": "off"
"no-void": "off",
"unicorn/catch-error-name": "off",
"unicorn/explicit-length-check": "off",
"unicorn/no-array-for-each": "off",
"unicorn/no-await-expression-member": "off",
"unicorn/no-null": "off",
"unicorn/prefer-export-from": "off",
"unicorn/prefer-number-properties": "off",
"unicorn/prefer-set-has": "off",
"unicorn/prefer-type-error": "off"
},
"env": {
"mocha": true
Expand Down
8 changes: 6 additions & 2 deletions packages/upload-client/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ export async function add(
* has the capability to perform the action.
*
* The issuer needs the `store/list` delegated capability.
* @param {import('./types').RequestOptions} [options]
* @param {import('./types').ListRequestOptions} [options]
* @returns {Promise<import('./types').ListResponse<import('./types').StoreListResult>>}
*/
export async function list(
{ issuer, with: resource, proofs, audience = servicePrincipal },
Expand All @@ -130,7 +131,10 @@ export async function list(
audience,
with: resource,
proofs,
nb: {},
nb: {
cursor: options.cursor,
size: options.size,
},
})
.execute(conn)

Expand Down
31 changes: 23 additions & 8 deletions packages/upload-client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,23 @@ export interface StoreAddResponse {
}

export interface ListResponse<R> {
count: number
page: number
pageSize: number
cursor?: string
size: number
results?: R[]
}

export interface StoreListResult {
payloadCID: CARLink
payloadCID: string
origin?: string
size: number
uploadedAt: number
uploadedAt: string
}

export interface UploadListResult {
carCID: CARLink
dataCID: Link<unknown, number, number, Version>
uploadedAt: number
uploaderDID: string
dataCID: string
carCID: string
uploadedAt: string
}

export interface InvocationConfig {
Expand Down Expand Up @@ -150,8 +151,22 @@ export interface Connectable {
connection?: ConnectionView<Service>
}

export interface Pageable {
/**
* Opaque string specifying where to start retrival of the next page of
* results.
*/
cursor?: string
/**
* Maximum number of items to return.
*/
size?: number
}

export interface RequestOptions extends Retryable, Abortable, Connectable {}

export interface ListRequestOptions extends RequestOptions, Pageable {}

export interface ShardingOptions {
/**
* The target shard size. Actual size of CAR output may be bigger due to CAR
Expand Down
8 changes: 6 additions & 2 deletions packages/upload-client/src/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ export async function add(
* has the capability to perform the action.
*
* The issuer needs the `upload/list` delegated capability.
* @param {import('./types').RequestOptions} [options]
* @param {import('./types').ListRequestOptions} [options]
* @returns {Promise<import('./types').ListResponse<import('./types').UploadListResult>>}
*/
export async function list(
{ issuer, with: resource, proofs, audience = servicePrincipal },
Expand All @@ -90,7 +91,10 @@ export async function list(
audience,
with: resource,
proofs,
nb: {},
nb: {
cursor: options.cursor,
size: options.size,
},
})
.execute(conn)

Expand Down
113 changes: 101 additions & 12 deletions packages/upload-client/test/store.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,13 @@ describe('Store.list', () => {
it('lists stored CAR files', async () => {
const car = await randomCAR(128)
const res = {
page: 1,
pageSize: 1000,
count: 1,
cursor: 'test',
size: 1000,
results: [
{
payloadCID: car.cid,
payloadCID: car.cid.toString(),
size: 123,
uploadedAt: Date.now(),
uploadedAt: new Date().toISOString(),
},
],
}
Expand Down Expand Up @@ -348,21 +347,111 @@ describe('Store.list', () => {
assert(service.store.list.called)
assert.equal(service.store.list.callCount, 1)

assert.equal(list.count, res.count)
assert.equal(list.page, res.page)
assert.equal(list.pageSize, res.pageSize)
assert.equal(list.cursor, res.cursor)
assert.equal(list.size, res.size)
assert(list.results)
assert.equal(list.results.length, res.results.length)
list.results.forEach((r, i) => {
assert.equal(
r.payloadCID.toString(),
res.results[i].payloadCID.toString()
)
assert.equal(r.payloadCID, res.results[i].payloadCID)
assert.equal(r.size, res.results[i].size)
assert.equal(r.uploadedAt, res.results[i].uploadedAt)
})
})

it('paginates', async () => {
const cursor = 'test'
const page0 = {
cursor,
size: 1,
results: [
{
payloadCID: (await randomCAR(128)).cid.toString(),
size: 123,
uploadedAt: new Date().toISOString(),
},
],
}
const page1 = {
size: 1,
results: [
{
payloadCID: (await randomCAR(128)).cid.toString(),
size: 123,
uploadedAt: new Date().toISOString(),
},
],
}

const space = await Signer.generate()
const agent = await Signer.generate()

const proofs = [
await StoreCapabilities.list.delegate({
issuer: space,
audience: agent,
with: space.did(),
expiration: Infinity,
}),
]

const service = mockService({
store: {
list: provide(StoreCapabilities.list, ({ invocation }) => {
assert.equal(invocation.issuer.did(), agent.did())
assert.equal(invocation.capabilities.length, 1)
const invCap = invocation.capabilities[0]
assert.equal(invCap.can, StoreCapabilities.list.can)
assert.equal(invCap.with, space.did())
assert.equal(invCap.nb?.size, 1)
return invCap.nb?.cursor === cursor ? page1 : page0
}),
},
})

const server = Server.create({
id: serviceSigner,
service,
decoder: CAR,
encoder: CBOR,
})
const connection = Client.connect({
id: serviceSigner,
encoder: CAR,
decoder: CBOR,
channel: server,
})

const results0 = await Store.list(
{ issuer: agent, with: space.did(), proofs, audience: serviceSigner },
{ size: 1, connection }
)
const results1 = await Store.list(
{ issuer: agent, with: space.did(), proofs, audience: serviceSigner },
{ size: 1, cursor: results0.cursor, connection }
)

assert(service.store.list.called)
assert.equal(service.store.list.callCount, 2)

assert.equal(results0.cursor, cursor)
assert(results0.results)
assert.equal(results0.results.length, page0.results.length)
results0.results.forEach((r, i) => {
assert.equal(r.payloadCID, page0.results[i].payloadCID)
assert.equal(r.size, page0.results[i].size)
assert.equal(r.uploadedAt, page0.results[i].uploadedAt)
})

assert(results1.results)
assert.equal(results1.cursor, undefined)
assert.equal(results1.results.length, page1.results.length)
results1.results.forEach((r, i) => {
assert.equal(r.payloadCID, page1.results[i].payloadCID)
assert.equal(r.size, page1.results[i].size)
assert.equal(r.uploadedAt, page1.results[i].uploadedAt)
})
})

it('throws on service error', async () => {
const space = await Signer.generate()
const agent = await Signer.generate()
Expand Down
Loading

0 comments on commit a5296a6

Please sign in to comment.