Skip to content

Commit cfa1faf

Browse files
committed
feat(storage): expose purge cache for entire bucket
1 parent d9043a4 commit cfa1faf

2 files changed

Lines changed: 55 additions & 11 deletions

File tree

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ export default class StorageFileApi extends BaseApiClient<StorageError> {
11611161
}
11621162

11631163
/**
1164-
* Purges the CDN cache for a single object in this bucket.
1164+
* Purges the CDN cache for an entire bucket or single object in this bucket.
11651165
*
11661166
* Maps to `DELETE /cdn/{bucket}/{path}` on the Storage API. The server
11671167
* issues a CDN invalidation for the object and returns `{ message: 'success' }`.
@@ -1174,12 +1174,12 @@ export default class StorageFileApi extends BaseApiClient<StorageError> {
11741174
* have `CDN_PURGE_ENDPOINT_URL` configured and the `purgeCache` tenant
11751175
* feature enabled, otherwise the server returns an error.
11761176
*
1177-
* Operates on a single object path. There is no wildcard or recursion: pass
1178-
* the exact path of the object you want invalidated.
1177+
* Operates on an entire bucket or a single object path. There is no wildcard or recursion: pass
1178+
* the exact path of the object you want invalidated or none to invalidate the entire bucket.
11791179
*
11801180
* @category Storage
11811181
* @subcategory File Buckets
1182-
* @param path The path (relative to the bucket) of the object to purge, e.g. `folder/avatar.png`.
1182+
* @param options Purge options including the object path (relative to the bucket) of the object to purge, e.g. `folder/avatar.png`. Purges cache for the entire bucket if omitted
11831183
* @param parameters Optional fetch parameters such as an `AbortController` signal.
11841184
* @returns Promise with `{ data: { message }, error: null }` on success or `{ data: null, error }` on failure.
11851185
*
@@ -1192,7 +1192,7 @@ export default class StorageFileApi extends BaseApiClient<StorageError> {
11921192
* ```
11931193
*/
11941194
async purgeCache(
1195-
path: string,
1195+
options?: { object: string },
11961196
parameters?: FetchParameters
11971197
): Promise<
11981198
| {
@@ -1205,10 +1205,12 @@ export default class StorageFileApi extends BaseApiClient<StorageError> {
12051205
}
12061206
> {
12071207
return this.handleOperation(async () => {
1208-
const _path = this._getFinalPath(path)
1208+
const purgePath =
1209+
options?.object === undefined ? this.bucketId : this._getFinalPath(options.object)
1210+
12091211
return await remove(
12101212
this.fetch,
1211-
`${this.url}/cdn/${_path}`,
1213+
`${this.url}/cdn/${purgePath}`,
12121214
{},
12131215
{ headers: this.headers },
12141216
parameters

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

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,7 +1033,7 @@ describe('purgeCache', () => {
10331033
jest.restoreAllMocks()
10341034
})
10351035

1036-
it('issues DELETE to /cdn/{bucket}/{path} and returns the server message', async () => {
1036+
it('issues DELETE to /cdn/{bucket}/{path} for single object purge and returns the server message', async () => {
10371037
const fetchMock = jest.fn().mockResolvedValue(
10381038
new Response(JSON.stringify({ message: 'success' }), {
10391039
status: 200,
@@ -1043,7 +1043,7 @@ describe('purgeCache', () => {
10431043
global.fetch = fetchMock
10441044

10451045
const client = new StorageClient(PURGE_URL, { apikey: 'service-role-token' })
1046-
const { data, error } = await client.from(BUCKET).purgeCache(PATH)
1046+
const { data, error } = await client.from(BUCKET).purgeCache({ object: PATH })
10471047

10481048
expect(error).toBeNull()
10491049
expect(data?.message).toBe('success')
@@ -1053,6 +1053,46 @@ describe('purgeCache', () => {
10531053
)
10541054
})
10551055

1056+
it('issues DELETE to /cdn/{bucket}/{path} EMPTY STRING for single object purge and returns the server message', async () => {
1057+
const fetchMock = jest.fn().mockResolvedValue(
1058+
new Response(JSON.stringify({ message: 'success' }), {
1059+
status: 200,
1060+
headers: { 'Content-Type': 'application/json' },
1061+
})
1062+
)
1063+
global.fetch = fetchMock
1064+
1065+
const client = new StorageClient(PURGE_URL, { apikey: 'service-role-token' })
1066+
const { data, error } = await client.from(BUCKET).purgeCache({ object: '' })
1067+
1068+
expect(error).toBeNull()
1069+
expect(data?.message).toBe('success')
1070+
expect(fetchMock).toHaveBeenCalledWith(
1071+
`${PURGE_URL}/cdn/${BUCKET}/`,
1072+
expect.objectContaining({ method: 'DELETE' })
1073+
)
1074+
})
1075+
1076+
it('issues DELETE to /cdn/{bucket} for bucket purge and returns the server message', async () => {
1077+
const fetchMock = jest.fn().mockResolvedValue(
1078+
new Response(JSON.stringify({ message: 'success' }), {
1079+
status: 200,
1080+
headers: { 'Content-Type': 'application/json' },
1081+
})
1082+
)
1083+
global.fetch = fetchMock
1084+
1085+
const client = new StorageClient(PURGE_URL, { apikey: 'service-role-token' })
1086+
const { data, error } = await client.from(BUCKET).purgeCache()
1087+
1088+
expect(error).toBeNull()
1089+
expect(data?.message).toBe('success')
1090+
expect(fetchMock).toHaveBeenCalledWith(
1091+
`${PURGE_URL}/cdn/${BUCKET}`,
1092+
expect.objectContaining({ method: 'DELETE' })
1093+
)
1094+
})
1095+
10561096
it('surfaces server errors via StorageApiError', async () => {
10571097
const fetchMock = jest
10581098
.fn()
@@ -1065,7 +1105,7 @@ describe('purgeCache', () => {
10651105
global.fetch = fetchMock
10661106

10671107
const client = new StorageClient(PURGE_URL, { apikey: 'service-role-token' })
1068-
const { data, error } = await client.from(BUCKET).purgeCache(PATH)
1108+
const { data, error } = await client.from(BUCKET).purgeCache({ object: PATH })
10691109

10701110
expect(data).toBeNull()
10711111
expect(error).toBeInstanceOf(StorageApiError)
@@ -1083,7 +1123,9 @@ describe('purgeCache', () => {
10831123

10841124
const client = new StorageClient(PURGE_URL, { apikey: 'service-role-token' })
10851125
const controller = new AbortController()
1086-
const { error } = await client.from(BUCKET).purgeCache(PATH, { signal: controller.signal })
1126+
const { error } = await client
1127+
.from(BUCKET)
1128+
.purgeCache({ object: PATH }, { signal: controller.signal })
10871129

10881130
expect(error).toBeNull()
10891131
expect(fetchMock).toHaveBeenCalledWith(

0 commit comments

Comments
 (0)