From 1cbbc626b8ebece67947149e3299db5a27463366 Mon Sep 17 00:00:00 2001 From: Lukas Klingsbo Date: Thu, 16 Jul 2026 18:32:53 +0200 Subject: [PATCH] feat(storage): add purgeBucketCache to invalidate CDN cache for a whole bucket --- .../lib/src/storage_bucket_api.dart | 29 +++++++++++++++++++ packages/storage_client/test/basic_test.dart | 29 +++++++++++++++++++ sdk-compliance.yaml | 4 +++ 3 files changed, 62 insertions(+) diff --git a/packages/storage_client/lib/src/storage_bucket_api.dart b/packages/storage_client/lib/src/storage_bucket_api.dart index 03d54bc82..a69fab7c9 100644 --- a/packages/storage_client/lib/src/storage_bucket_api.dart +++ b/packages/storage_client/lib/src/storage_bucket_api.dart @@ -134,6 +134,35 @@ class StorageBucketApi { return (response as Map)['message'] as String; } + /// Purges the CDN cache for an entire bucket. + /// + /// Invalidates the CDN cache for every object in the bucket [id]. Maps to + /// `DELETE /cdn/{bucket}` on the storage server. + /// + /// When [transformations] is `true`, only the resized/formatted variants are + /// purged, leaving the original cached files intact. When omitted the bucket + /// cache is purged. + /// + /// Requires the service-role key and the tenant `purgeCache` feature to be + /// enabled on the storage server. + Future purgeBucketCache( + String id, { + bool transformations = false, + }) async { + var requestUrl = Uri.parse('$url/cdn/$id'); + if (transformations) { + requestUrl = requestUrl.replace( + queryParameters: {'transformations': 'true'}, + ); + } + final response = await storageFetch.delete( + requestUrl.toString(), + {}, + options: FetchOptions(headers), + ); + return (response as Map)['message'] as String; + } + /// Creates a new analytics bucket backed by the Apache Iceberg table format. /// /// [id] is the unique identifier for the bucket you are creating. diff --git a/packages/storage_client/test/basic_test.dart b/packages/storage_client/test/basic_test.dart index 48665fc4e..d1677e6e8 100644 --- a/packages/storage_client/test/basic_test.dart +++ b/packages/storage_client/test/basic_test.dart @@ -154,6 +154,35 @@ void main() { expect(response, 'Deleted'); }); + test('should purgeBucketCache issuing DELETE to /cdn/{bucket}', () async { + customHttpClient.response = {'message': 'success'}; + + final response = await client.purgeBucketCache('test_bucket'); + + final request = customHttpClient.receivedRequests.last; + expect(request.method, 'DELETE'); + expect( + request.url.toString(), + endsWith('/storage/v1/cdn/test_bucket'), + ); + expect(request.url.query, isEmpty); + expect(response, 'success'); + }); + + test('should purgeBucketCache with transformations query param', () async { + customHttpClient.response = {'message': 'success'}; + + final response = await client.purgeBucketCache( + 'test_bucket', + transformations: true, + ); + + final request = customHttpClient.receivedRequests.last; + expect(request.method, 'DELETE'); + expect(request.url.queryParameters['transformations'], 'true'); + expect(response, 'success'); + }); + test('should create analytics bucket', () async { customHttpClient.response = testAnalyticsBucketJson; diff --git a/sdk-compliance.yaml b/sdk-compliance.yaml index 9a91fb338..46bf23821 100644 --- a/sdk-compliance.yaml +++ b/sdk-compliance.yaml @@ -442,6 +442,10 @@ features: status: implemented symbols: - StorageFileApi.purgeCache + storage.file_buckets.purge_bucket_cache: + status: implemented + symbols: + - StorageBucketApi.purgeBucketCache storage.file_buckets.create_signed_url: status: implemented symbols: