Skip to content

Commit 3ad736b

Browse files
committed
feat(storage): add purgeCache to invalidate CDN cache for a single object
1 parent 6e11db6 commit 3ad736b

3 files changed

Lines changed: 66 additions & 0 deletions

File tree

packages/storage_client/lib/src/storage_file_api.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,37 @@ class StorageFileApi {
695695
return fileObjects;
696696
}
697697

698+
/// Purges the CDN cache for a single object.
699+
///
700+
/// Invalidates the CDN cache for the object at [path] (relative to the
701+
/// bucket). There is no wildcard or recursion; pass the exact path of the
702+
/// object to invalidate. For example: `purgeCache('folder/avatar.png')`.
703+
///
704+
/// When [transformations] is `true`, only the resized/formatted variants are
705+
/// purged, leaving the original cached file intact. When omitted the object
706+
/// cache is purged.
707+
///
708+
/// Requires the service-role key and the tenant `purgeCache` feature to be
709+
/// enabled on the storage server.
710+
Future<String> purgeCache(
711+
String path, {
712+
bool transformations = false,
713+
}) async {
714+
final finalPath = _getFinalPath(path);
715+
var requestUrl = Uri.parse('$url/cdn/$finalPath');
716+
if (transformations) {
717+
requestUrl = requestUrl.replace(
718+
queryParameters: {'transformations': 'true'},
719+
);
720+
}
721+
final response = await _storageFetch.delete(
722+
requestUrl.toString(),
723+
{},
724+
options: _fetchOptions,
725+
);
726+
return (response as Map<String, dynamic>)['message'] as String;
727+
}
728+
698729
/// Lists all the files within a bucket.
699730
///
700731
/// [path] The folder path.

packages/storage_client/test/basic_test.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,37 @@ void main() {
248248
expect(response, 'Move');
249249
});
250250

251+
test('should purgeCache issuing DELETE to /cdn/{bucket}/{path}', () async {
252+
customHttpClient.response = {'message': 'success'};
253+
254+
final response = await client.from('public').purgeCache('folder/a.txt');
255+
256+
final request = customHttpClient.receivedRequests.last;
257+
expect(request.method, 'DELETE');
258+
expect(
259+
request.url.toString(),
260+
endsWith('/storage/v1/cdn/public/folder/a.txt'),
261+
);
262+
expect(request.url.query, isEmpty);
263+
expect(response, 'success');
264+
});
265+
266+
test('should purgeCache with transformations query param', () async {
267+
customHttpClient.response = {'message': 'success'};
268+
269+
final response = await client
270+
.from('public')
271+
.purgeCache('folder/a.txt', transformations: true);
272+
273+
final request = customHttpClient.receivedRequests.last;
274+
expect(request.method, 'DELETE');
275+
expect(
276+
request.url.queryParameters['transformations'],
277+
'true',
278+
);
279+
expect(response, 'success');
280+
});
281+
251282
test('should createSignedUrl file', () async {
252283
customHttpClient.response = {'signedURL': '/signed/url'};
253284

sdk-compliance.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,10 @@ features:
438438
storage.file_buckets.copy: implemented
439439
storage.file_buckets.copy_cross_bucket: implemented
440440
storage.file_buckets.remove: implemented
441+
storage.file_buckets.purge_cache:
442+
status: implemented
443+
symbols:
444+
- StorageFileApi.purgeCache
441445
storage.file_buckets.create_signed_url:
442446
status: implemented
443447
symbols:

0 commit comments

Comments
 (0)