Skip to content

Commit be48eac

Browse files
committed
feat(storage): add support for bucket pagination and sorting
1 parent c408c47 commit be48eac

File tree

3 files changed

+161
-3
lines changed

3 files changed

+161
-3
lines changed

packages/core/storage-js/src/lib/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ export interface Bucket {
1919
public: boolean
2020
}
2121

22+
export interface ListBucketOptions {
23+
limit?: number
24+
offset?: number
25+
sortColumn?: 'id' | 'name' | 'created_at' | 'updated_at'
26+
sortOrder?: 'asc' | 'desc'
27+
search?: string
28+
}
29+
2230
/**
2331
* Represents an Analytics Bucket using Apache Iceberg table format.
2432
* Analytics buckets are optimized for analytical queries and data processing.

packages/core/storage-js/src/packages/StorageBucketApi.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { DEFAULT_HEADERS } from '../lib/constants'
22
import { isStorageError, StorageError } from '../lib/errors'
33
import { Fetch, get, post, put, remove } from '../lib/fetch'
44
import { resolveFetch } from '../lib/helpers'
5-
import { Bucket, BucketType } from '../lib/types'
5+
import { Bucket, BucketType, ListBucketOptions } from '../lib/types'
66
import { StorageClientOptions } from '../StorageClient'
77

88
export default class StorageBucketApi {
@@ -44,7 +44,7 @@ export default class StorageBucketApi {
4444
/**
4545
* Retrieves the details of all Storage buckets within an existing project.
4646
*/
47-
async listBuckets(): Promise<
47+
async listBuckets(options?: ListBucketOptions): Promise<
4848
| {
4949
data: Bucket[]
5050
error: null
@@ -55,7 +55,10 @@ export default class StorageBucketApi {
5555
}
5656
> {
5757
try {
58-
const data = await get(this.fetch, `${this.url}/bucket`, { headers: this.headers })
58+
const queryString = this.listBucketOptionsToQueryString(options)
59+
const data = await get(this.fetch, `${this.url}/bucket${queryString}`, {
60+
headers: this.headers,
61+
})
5962
return { data, error: null }
6063
} catch (error) {
6164
if (this.shouldThrowOnError) {
@@ -286,4 +289,26 @@ export default class StorageBucketApi {
286289
throw error
287290
}
288291
}
292+
293+
private listBucketOptionsToQueryString(options?: ListBucketOptions): string {
294+
const params: Record<string, string> = {}
295+
if (options) {
296+
if ('limit' in options) {
297+
params.limit = String(options.limit)
298+
}
299+
if ('offset' in options) {
300+
params.offset = String(options.offset)
301+
}
302+
if (options.search) {
303+
params.search = options.search
304+
}
305+
if (options.sortColumn) {
306+
params.sortColumn = options.sortColumn
307+
}
308+
if (options.sortOrder) {
309+
params.sortOrder = options.sortOrder
310+
}
311+
}
312+
return Object.keys(params).length > 0 ? '?' + new URLSearchParams(params).toString() : ''
313+
}
289314
}

packages/core/storage-js/test/storageBucketApi.test.ts

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,131 @@ describe('Bucket API Error Handling', () => {
142142
// Clean up
143143
mockFn.mockRestore()
144144
})
145+
146+
it('constructs query string with limit option', async () => {
147+
const storage = new StorageClient(URL, { apikey: KEY })
148+
const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([])
149+
150+
await storage.listBuckets({ limit: 10 })
151+
152+
expect(mockGet).toHaveBeenCalledWith(
153+
expect.any(Function),
154+
`${URL}/bucket?limit=10`,
155+
expect.objectContaining({
156+
headers: expect.any(Object),
157+
})
158+
)
159+
160+
mockGet.mockRestore()
161+
})
162+
163+
it('constructs query string with offset option', async () => {
164+
const storage = new StorageClient(URL, { apikey: KEY })
165+
const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([])
166+
167+
await storage.listBuckets({ offset: 5 })
168+
169+
expect(mockGet).toHaveBeenCalledWith(
170+
expect.any(Function),
171+
`${URL}/bucket?offset=5`,
172+
expect.objectContaining({
173+
headers: expect.any(Object),
174+
})
175+
)
176+
177+
mockGet.mockRestore()
178+
})
179+
180+
it('constructs query string with sortColumn option', async () => {
181+
const storage = new StorageClient(URL, { apikey: KEY })
182+
const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([])
183+
184+
await storage.listBuckets({ sortColumn: 'name' })
185+
186+
expect(mockGet).toHaveBeenCalledWith(
187+
expect.any(Function),
188+
`${URL}/bucket?sortColumn=name`,
189+
expect.objectContaining({
190+
headers: expect.any(Object),
191+
})
192+
)
193+
194+
mockGet.mockRestore()
195+
})
196+
197+
it('constructs query string with sortOrder option', async () => {
198+
const storage = new StorageClient(URL, { apikey: KEY })
199+
const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([])
200+
201+
await storage.listBuckets({ sortOrder: 'desc' })
202+
203+
expect(mockGet).toHaveBeenCalledWith(
204+
expect.any(Function),
205+
`${URL}/bucket?sortOrder=desc`,
206+
expect.objectContaining({
207+
headers: expect.any(Object),
208+
})
209+
)
210+
211+
mockGet.mockRestore()
212+
})
213+
214+
it('constructs query string with search option', async () => {
215+
const storage = new StorageClient(URL, { apikey: KEY })
216+
const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([])
217+
218+
await storage.listBuckets({ search: 'test-bucket' })
219+
220+
expect(mockGet).toHaveBeenCalledWith(
221+
expect.any(Function),
222+
`${URL}/bucket?search=test-bucket`,
223+
expect.objectContaining({
224+
headers: expect.any(Object),
225+
})
226+
)
227+
228+
mockGet.mockRestore()
229+
})
230+
231+
it('constructs query string with multiple options', async () => {
232+
const storage = new StorageClient(URL, { apikey: KEY })
233+
const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([])
234+
235+
await storage.listBuckets({
236+
limit: 20,
237+
offset: 10,
238+
sortColumn: 'created_at',
239+
sortOrder: 'asc',
240+
search: 'my-bucket',
241+
})
242+
243+
expect(mockGet).toHaveBeenCalledWith(
244+
expect.any(Function),
245+
`${URL}/bucket?limit=20&offset=10&search=my-bucket&sortColumn=created_at&sortOrder=asc`,
246+
expect.objectContaining({
247+
headers: expect.any(Object),
248+
})
249+
)
250+
251+
mockGet.mockRestore()
252+
})
253+
254+
it('handles empty options object', async () => {
255+
const storage = new StorageClient(URL, { apikey: KEY })
256+
const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([])
257+
258+
await storage.listBuckets({})
259+
260+
expect(mockGet).toHaveBeenCalledWith(
261+
expect.any(Function),
262+
`${URL}/bucket`,
263+
expect.objectContaining({
264+
headers: expect.any(Object),
265+
})
266+
)
267+
268+
mockGet.mockRestore()
269+
})
145270
})
146271

147272
describe('getBucket', () => {

0 commit comments

Comments
 (0)