Skip to content

Commit a814b7d

Browse files
authored
feat: add support for .throwOnError() (#250)
1 parent f795e1c commit a814b7d

File tree

5 files changed

+187
-6
lines changed

5 files changed

+187
-6
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,21 @@ const storageClient = new StorageClient(STORAGE_URL, {
156156
const { data, error } = await storageClient.from('public-bucket').getPublicUrl('path/to/file')
157157
```
158158

159+
### Error Handling
160+
161+
Supplying `.throwOnError()` will throw errors instead of returning them as a property on the response object.
162+
163+
```js
164+
try {
165+
const { data } = await storageClient
166+
.from('bucket')
167+
.throwOnError()
168+
.download('path/to/file')
169+
} catch (error) {
170+
console.error(error)
171+
}
172+
```
173+
159174
## Sponsors
160175

161176
We are building the features of Firebase using enterprise-grade, open source products. We support existing communities wherever possible, and if the products don’t exist we build them and open source them ourselves. Thanks to these sponsors who are making the OSS ecosystem better for everyone.

src/packages/StorageBucketApi.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export default class StorageBucketApi {
99
protected url: string
1010
protected headers: { [key: string]: string }
1111
protected fetch: Fetch
12+
protected shouldThrowOnError = false
1213

1314
constructor(
1415
url: string,
@@ -32,6 +33,14 @@ export default class StorageBucketApi {
3233
this.fetch = resolveFetch(fetch)
3334
}
3435

36+
/**
37+
* Enable throwing errors instead of returning them.
38+
*/
39+
public throwOnError(): this {
40+
this.shouldThrowOnError = true
41+
return this
42+
}
43+
3544
/**
3645
* Retrieves the details of all Storage buckets within an existing project.
3746
*/
@@ -49,6 +58,9 @@ export default class StorageBucketApi {
4958
const data = await get(this.fetch, `${this.url}/bucket`, { headers: this.headers })
5059
return { data, error: null }
5160
} catch (error) {
61+
if (this.shouldThrowOnError) {
62+
throw error
63+
}
5264
if (isStorageError(error)) {
5365
return { data: null, error }
5466
}
@@ -78,6 +90,9 @@ export default class StorageBucketApi {
7890
const data = await get(this.fetch, `${this.url}/bucket/${id}`, { headers: this.headers })
7991
return { data, error: null }
8092
} catch (error) {
93+
if (this.shouldThrowOnError) {
94+
throw error
95+
}
8196
if (isStorageError(error)) {
8297
return { data: null, error }
8398
}
@@ -137,6 +152,9 @@ export default class StorageBucketApi {
137152
)
138153
return { data, error: null }
139154
} catch (error) {
155+
if (this.shouldThrowOnError) {
156+
throw error
157+
}
140158
if (isStorageError(error)) {
141159
return { data: null, error }
142160
}
@@ -189,6 +207,9 @@ export default class StorageBucketApi {
189207
)
190208
return { data, error: null }
191209
} catch (error) {
210+
if (this.shouldThrowOnError) {
211+
throw error
212+
}
192213
if (isStorageError(error)) {
193214
return { data: null, error }
194215
}
@@ -223,6 +244,9 @@ export default class StorageBucketApi {
223244
)
224245
return { data, error: null }
225246
} catch (error) {
247+
if (this.shouldThrowOnError) {
248+
throw error
249+
}
226250
if (isStorageError(error)) {
227251
return { data: null, error }
228252
}
@@ -258,6 +282,9 @@ export default class StorageBucketApi {
258282
)
259283
return { data, error: null }
260284
} catch (error) {
285+
if (this.shouldThrowOnError) {
286+
throw error
287+
}
261288
if (isStorageError(error)) {
262289
return { data: null, error }
263290
}

src/packages/StorageFileApi.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export default class StorageFileApi {
4646
protected headers: { [key: string]: string }
4747
protected bucketId?: string
4848
protected fetch: Fetch
49+
protected shouldThrowOnError = false
4950

5051
constructor(
5152
url: string,
@@ -59,6 +60,14 @@ export default class StorageFileApi {
5960
this.fetch = resolveFetch(fetch)
6061
}
6162

63+
/**
64+
* Enable throwing errors instead of returning them.
65+
*/
66+
public throwOnError(): this {
67+
this.shouldThrowOnError = true
68+
return this
69+
}
70+
6271
/**
6372
* Uploads a file to an existing bucket or replaces an existing file at the specified path with a new one.
6473
*
@@ -132,6 +141,9 @@ export default class StorageFileApi {
132141
error: null,
133142
}
134143
} catch (error) {
144+
if (this.shouldThrowOnError) {
145+
throw error
146+
}
135147
if (isStorageError(error)) {
136148
return { data: null, error }
137149
}
@@ -209,6 +221,9 @@ export default class StorageFileApi {
209221
error: null,
210222
}
211223
} catch (error) {
224+
if (this.shouldThrowOnError) {
225+
throw error
226+
}
212227
if (isStorageError(error)) {
213228
return { data: null, error }
214229
}
@@ -263,6 +278,9 @@ export default class StorageFileApi {
263278

264279
return { data: { signedUrl: url.toString(), path, token }, error: null }
265280
} catch (error) {
281+
if (this.shouldThrowOnError) {
282+
throw error
283+
}
266284
if (isStorageError(error)) {
267285
return { data: null, error }
268286
}
@@ -339,6 +357,9 @@ export default class StorageFileApi {
339357
)
340358
return { data, error: null }
341359
} catch (error) {
360+
if (this.shouldThrowOnError) {
361+
throw error
362+
}
342363
if (isStorageError(error)) {
343364
return { data: null, error }
344365
}
@@ -382,6 +403,9 @@ export default class StorageFileApi {
382403
)
383404
return { data: { path: data.Key }, error: null }
384405
} catch (error) {
406+
if (this.shouldThrowOnError) {
407+
throw error
408+
}
385409
if (isStorageError(error)) {
386410
return { data: null, error }
387411
}
@@ -428,6 +452,9 @@ export default class StorageFileApi {
428452
data = { signedUrl }
429453
return { data, error: null }
430454
} catch (error) {
455+
if (this.shouldThrowOnError) {
456+
throw error
457+
}
431458
if (isStorageError(error)) {
432459
return { data: null, error }
433460
}
@@ -478,6 +505,9 @@ export default class StorageFileApi {
478505
error: null,
479506
}
480507
} catch (error) {
508+
if (this.shouldThrowOnError) {
509+
throw error
510+
}
481511
if (isStorageError(error)) {
482512
return { data: null, error }
483513
}
@@ -519,6 +549,9 @@ export default class StorageFileApi {
519549
const data = await res.blob()
520550
return { data, error: null }
521551
} catch (error) {
552+
if (this.shouldThrowOnError) {
553+
throw error
554+
}
522555
if (isStorageError(error)) {
523556
return { data: null, error }
524557
}
@@ -552,6 +585,9 @@ export default class StorageFileApi {
552585

553586
return { data: recursiveToCamel(data) as Camelize<FileObjectV2>, error: null }
554587
} catch (error) {
588+
if (this.shouldThrowOnError) {
589+
throw error
590+
}
555591
if (isStorageError(error)) {
556592
return { data: null, error }
557593
}
@@ -585,6 +621,9 @@ export default class StorageFileApi {
585621

586622
return { data: true, error: null }
587623
} catch (error) {
624+
if (this.shouldThrowOnError) {
625+
throw error
626+
}
588627
if (isStorageError(error) && error instanceof StorageUnknownError) {
589628
const originalError = (error.originalError as unknown) as { status: number }
590629

@@ -664,6 +703,9 @@ export default class StorageFileApi {
664703
)
665704
return { data, error: null }
666705
} catch (error) {
706+
if (this.shouldThrowOnError) {
707+
throw error
708+
}
667709
if (isStorageError(error)) {
668710
return { data: null, error }
669711
}
@@ -765,6 +807,9 @@ export default class StorageFileApi {
765807
)
766808
return { data, error: null }
767809
} catch (error) {
810+
if (this.shouldThrowOnError) {
811+
throw error
812+
}
768813
if (isStorageError(error)) {
769814
return { data: null, error }
770815
}
@@ -802,6 +847,9 @@ export default class StorageFileApi {
802847
)
803848
return { data, error: null }
804849
} catch (error) {
850+
if (this.shouldThrowOnError) {
851+
throw error
852+
}
805853
if (isStorageError(error)) {
806854
return { data: null, error }
807855
}

test/storageBucketApi.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ describe('Bucket API Error Handling', () => {
8585
expect(data).toBeNull()
8686
expect(error).not.toBeNull()
8787
expect(error?.message).toBe(`headers must have required property 'authorization'`)
88+
89+
// throws when .throwOnError is enabled
90+
await expect(storage.throwOnError().listBuckets()).rejects.toThrowError(
91+
"headers must have required property 'authorization'"
92+
)
8893
})
8994

9095
it('handles network errors', async () => {
@@ -96,6 +101,9 @@ describe('Bucket API Error Handling', () => {
96101
expect(data).toBeNull()
97102
expect(error).not.toBeNull()
98103
expect(error?.message).toBe('Network failure')
104+
105+
// throws when .throwOnError is enabled
106+
await expect(storage.throwOnError().listBuckets()).rejects.toThrowError('Network failure')
99107
})
100108

101109
it('wraps non-Response errors as StorageUnknownError', async () => {
@@ -110,6 +118,9 @@ describe('Bucket API Error Handling', () => {
110118
expect(data).toBeNull()
111119
expect(error).toBeInstanceOf(StorageUnknownError)
112120
expect(error?.message).toBe('Invalid argument')
121+
122+
// throws when .throwOnError is enabled
123+
await expect(storage.throwOnError().listBuckets()).rejects.toThrowError('Invalid argument')
113124
})
114125

115126
it('throws non-StorageError exceptions', async () => {
@@ -142,6 +153,11 @@ describe('Bucket API Error Handling', () => {
142153
expect(data).toBeNull()
143154
expect(error).not.toBeNull()
144155
expect(error?.message).toBe('Network failure')
156+
157+
// throws when .throwOnError is enabled
158+
await expect(storage.throwOnError().getBucket('non-existent-bucket')).rejects.toThrow(
159+
'Network failure'
160+
)
145161
})
146162

147163
it('wraps non-Response errors as StorageUnknownError', async () => {
@@ -188,6 +204,11 @@ describe('Bucket API Error Handling', () => {
188204
expect(data).toBeNull()
189205
expect(error).not.toBeNull()
190206
expect(error?.message).toBe('Network failure')
207+
208+
// throws when .throwOnError is enabled
209+
await expect(storage.throwOnError().createBucket('new-bucket')).rejects.toThrow(
210+
'Network failure'
211+
)
191212
})
192213

193214
it('wraps non-Response errors as StorageUnknownError', async () => {
@@ -234,6 +255,11 @@ describe('Bucket API Error Handling', () => {
234255
expect(data).toBeNull()
235256
expect(error).not.toBeNull()
236257
expect(error?.message).toBe('Network failure')
258+
259+
// throws when .throwOnError is enabled
260+
await expect(
261+
storage.throwOnError().updateBucket('existing-bucket', { public: true })
262+
).rejects.toThrow('Network failure')
237263
})
238264

239265
it('wraps non-Response errors as StorageUnknownError', async () => {
@@ -280,6 +306,11 @@ describe('Bucket API Error Handling', () => {
280306
expect(data).toBeNull()
281307
expect(error).not.toBeNull()
282308
expect(error?.message).toBe('Network failure')
309+
310+
// throws when .throwOnError is enabled
311+
await expect(storage.throwOnError().emptyBucket('existing-bucket')).rejects.toThrow(
312+
'Network failure'
313+
)
283314
})
284315

285316
it('wraps non-Response errors as StorageUnknownError', async () => {
@@ -326,6 +357,11 @@ describe('Bucket API Error Handling', () => {
326357
expect(data).toBeNull()
327358
expect(error).not.toBeNull()
328359
expect(error?.message).toBe('Network failure')
360+
361+
// throws when .throwOnError is enabled
362+
await expect(storage.throwOnError().deleteBucket('existing-bucket')).rejects.toThrow(
363+
'Network failure'
364+
)
329365
})
330366

331367
it('wraps non-Response errors as StorageUnknownError', async () => {
@@ -338,6 +374,11 @@ describe('Bucket API Error Handling', () => {
338374
expect(data).toBeNull()
339375
expect(error).toBeInstanceOf(StorageUnknownError)
340376
expect(error?.message).toBe('Invalid delete operation')
377+
378+
// throws when .throwOnError is enabled
379+
await expect(storage.throwOnError().deleteBucket('test-bucket')).rejects.toThrow(
380+
'Invalid delete operation'
381+
)
341382
})
342383

343384
it('throws non-StorageError exceptions', async () => {

0 commit comments

Comments
 (0)