diff --git a/packages/core/storage-js/src/lib/types.ts b/packages/core/storage-js/src/lib/types.ts index 99a2bd8f..5c0f7ab7 100644 --- a/packages/core/storage-js/src/lib/types.ts +++ b/packages/core/storage-js/src/lib/types.ts @@ -19,6 +19,14 @@ export interface Bucket { public: boolean } +export interface ListBucketOptions { + limit?: number + offset?: number + sortColumn?: 'id' | 'name' | 'created_at' | 'updated_at' + sortOrder?: 'asc' | 'desc' + search?: string +} + /** * Represents an Analytics Bucket using Apache Iceberg table format. * Analytics buckets are optimized for analytical queries and data processing. diff --git a/packages/core/storage-js/src/packages/StorageBucketApi.ts b/packages/core/storage-js/src/packages/StorageBucketApi.ts index a2b7157f..0a9d5d9c 100644 --- a/packages/core/storage-js/src/packages/StorageBucketApi.ts +++ b/packages/core/storage-js/src/packages/StorageBucketApi.ts @@ -2,7 +2,7 @@ import { DEFAULT_HEADERS } from '../lib/constants' import { isStorageError, StorageError } from '../lib/errors' import { Fetch, get, post, put, remove } from '../lib/fetch' import { resolveFetch } from '../lib/helpers' -import { Bucket, BucketType } from '../lib/types' +import { Bucket, BucketType, ListBucketOptions } from '../lib/types' import { StorageClientOptions } from '../StorageClient' export default class StorageBucketApi { @@ -44,7 +44,7 @@ export default class StorageBucketApi { /** * Retrieves the details of all Storage buckets within an existing project. */ - async listBuckets(): Promise< + async listBuckets(options?: ListBucketOptions): Promise< | { data: Bucket[] error: null @@ -55,7 +55,10 @@ export default class StorageBucketApi { } > { try { - const data = await get(this.fetch, `${this.url}/bucket`, { headers: this.headers }) + const queryString = this.listBucketOptionsToQueryString(options) + const data = await get(this.fetch, `${this.url}/bucket${queryString}`, { + headers: this.headers, + }) return { data, error: null } } catch (error) { if (this.shouldThrowOnError) { @@ -286,4 +289,26 @@ export default class StorageBucketApi { throw error } } + + private listBucketOptionsToQueryString(options?: ListBucketOptions): string { + const params: Record = {} + if (options) { + if ('limit' in options) { + params.limit = String(options.limit) + } + if ('offset' in options) { + params.offset = String(options.offset) + } + if (options.search) { + params.search = options.search + } + if (options.sortColumn) { + params.sortColumn = options.sortColumn + } + if (options.sortOrder) { + params.sortOrder = options.sortOrder + } + } + return Object.keys(params).length > 0 ? '?' + new URLSearchParams(params).toString() : '' + } } diff --git a/packages/core/storage-js/test/storageBucketApi.test.ts b/packages/core/storage-js/test/storageBucketApi.test.ts index ff9adf42..1e565ebc 100644 --- a/packages/core/storage-js/test/storageBucketApi.test.ts +++ b/packages/core/storage-js/test/storageBucketApi.test.ts @@ -142,6 +142,131 @@ describe('Bucket API Error Handling', () => { // Clean up mockFn.mockRestore() }) + + it('constructs query string with limit option', async () => { + const storage = new StorageClient(URL, { apikey: KEY }) + const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([]) + + await storage.listBuckets({ limit: 10 }) + + expect(mockGet).toHaveBeenCalledWith( + expect.any(Function), + `${URL}/bucket?limit=10`, + expect.objectContaining({ + headers: expect.any(Object), + }) + ) + + mockGet.mockRestore() + }) + + it('constructs query string with offset option', async () => { + const storage = new StorageClient(URL, { apikey: KEY }) + const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([]) + + await storage.listBuckets({ offset: 5 }) + + expect(mockGet).toHaveBeenCalledWith( + expect.any(Function), + `${URL}/bucket?offset=5`, + expect.objectContaining({ + headers: expect.any(Object), + }) + ) + + mockGet.mockRestore() + }) + + it('constructs query string with sortColumn option', async () => { + const storage = new StorageClient(URL, { apikey: KEY }) + const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([]) + + await storage.listBuckets({ sortColumn: 'name' }) + + expect(mockGet).toHaveBeenCalledWith( + expect.any(Function), + `${URL}/bucket?sortColumn=name`, + expect.objectContaining({ + headers: expect.any(Object), + }) + ) + + mockGet.mockRestore() + }) + + it('constructs query string with sortOrder option', async () => { + const storage = new StorageClient(URL, { apikey: KEY }) + const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([]) + + await storage.listBuckets({ sortOrder: 'desc' }) + + expect(mockGet).toHaveBeenCalledWith( + expect.any(Function), + `${URL}/bucket?sortOrder=desc`, + expect.objectContaining({ + headers: expect.any(Object), + }) + ) + + mockGet.mockRestore() + }) + + it('constructs query string with search option', async () => { + const storage = new StorageClient(URL, { apikey: KEY }) + const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([]) + + await storage.listBuckets({ search: 'test-bucket' }) + + expect(mockGet).toHaveBeenCalledWith( + expect.any(Function), + `${URL}/bucket?search=test-bucket`, + expect.objectContaining({ + headers: expect.any(Object), + }) + ) + + mockGet.mockRestore() + }) + + it('constructs query string with multiple options', async () => { + const storage = new StorageClient(URL, { apikey: KEY }) + const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([]) + + await storage.listBuckets({ + limit: 20, + offset: 10, + sortColumn: 'created_at', + sortOrder: 'asc', + search: 'my-bucket', + }) + + expect(mockGet).toHaveBeenCalledWith( + expect.any(Function), + `${URL}/bucket?limit=20&offset=10&search=my-bucket&sortColumn=created_at&sortOrder=asc`, + expect.objectContaining({ + headers: expect.any(Object), + }) + ) + + mockGet.mockRestore() + }) + + it('handles empty options object', async () => { + const storage = new StorageClient(URL, { apikey: KEY }) + const mockGet = jest.spyOn(require('../src/lib/fetch'), 'get').mockResolvedValue([]) + + await storage.listBuckets({}) + + expect(mockGet).toHaveBeenCalledWith( + expect.any(Function), + `${URL}/bucket`, + expect.objectContaining({ + headers: expect.any(Object), + }) + ) + + mockGet.mockRestore() + }) }) describe('getBucket', () => {