Skip to content

Commit e095c14

Browse files
authored
feat(storage_client): Support copy/move to different bucket (#1043)
* ci: update infra to state of storage-js * feat(storage_client): support copy/move to different bucket
1 parent 8f473f1 commit e095c14

File tree

6 files changed

+91
-18
lines changed

6 files changed

+91
-18
lines changed

infra/storage_client/docker-compose.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,14 @@ services:
3838
FILE_STORAGE_BACKEND_PATH: /tmp/storage
3939
ENABLE_IMAGE_TRANSFORMATION: "true"
4040
IMGPROXY_URL: http://imgproxy:8080
41+
DEBUG: "knex:*"
42+
4143
volumes:
4244
- assets-volume:/tmp/storage
4345
healthcheck:
4446
test: ['CMD-SHELL', 'curl -f -LI http://localhost:5000/status']
47+
interval: 2s
48+
4549
db:
4650
build:
4751
context: ./postgres
@@ -62,6 +66,20 @@ services:
6266
timeout: 5s
6367
retries: 5
6468

69+
dummy_data:
70+
build:
71+
context: ./postgres
72+
depends_on:
73+
storage:
74+
condition: service_healthy
75+
volumes:
76+
- ./postgres:/sql
77+
command:
78+
- psql
79+
- "postgresql://postgres:postgres@db:5432/postgres"
80+
- -f
81+
- /sql/dummy-data.sql
82+
6583
imgproxy:
6684
image: darthsim/imgproxy
6785
ports:
@@ -73,4 +91,4 @@ services:
7391
- IMGPROXY_USE_ETAG=true
7492
- IMGPROXY_ENABLE_WEBP_DETECTION=true
7593
volumes:
76-
assets-volume:
94+
assets-volume:

infra/storage_client/postgres/Dockerfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ FROM supabase/postgres:0.13.0
33
COPY 00-initial-schema.sql /docker-entrypoint-initdb.d/00-initial-schema.sql
44
COPY auth-schema.sql /docker-entrypoint-initdb.d/01-auth-schema.sql
55
COPY storage-schema.sql /docker-entrypoint-initdb.d/02-storage-schema.sql
6-
COPY dummy-data.sql /docker-entrypoint-initdb.d/03-dummy-data.sql
76

87
# Build time defaults
98
ARG build_POSTGRES_DB=postgres
@@ -17,4 +16,4 @@ ENV POSTGRES_USER=$build_POSTGRES_USER
1716
ENV POSTGRES_PASSWORD=$build_POSTGRES_PASSWORD
1817
ENV POSTGRES_PORT=$build_POSTGRES_PORT
1918

20-
EXPOSE 5432
19+
EXPOSE 5432

infra/storage_client/postgres/storage-schema.sql

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ CREATE TABLE "storage"."objects" (
2828
"last_accessed_at" timestamptz DEFAULT now(),
2929
"metadata" jsonb,
3030
CONSTRAINT "objects_bucketId_fkey" FOREIGN KEY ("bucket_id") REFERENCES "storage"."buckets"("id"),
31-
CONSTRAINT "objects_owner_fkey" FOREIGN KEY ("owner") REFERENCES "auth"."users"("id"),
3231
PRIMARY KEY ("id")
3332
);
3433
CREATE UNIQUE INDEX "bucketid_objname" ON "storage"."objects" USING BTREE ("bucket_id","name");
@@ -85,27 +84,24 @@ CREATE OR REPLACE FUNCTION storage.search(prefix text, bucketname text, limits i
8584
)
8685
LANGUAGE plpgsql
8786
AS $function$
88-
DECLARE
89-
_bucketId text;
9087
BEGIN
91-
select buckets."id" from buckets where buckets.name=bucketname limit 1 into _bucketId;
92-
return query
88+
return query
9389
with files_folders as (
9490
select ((string_to_array(objects.name, '/'))[levels]) as folder
9591
from objects
9692
where objects.name ilike prefix || '%'
97-
and bucket_id = _bucketId
93+
and bucket_id = bucketname
9894
GROUP by folder
9995
limit limits
10096
offset offsets
101-
)
102-
select files_folders.folder as name, objects.id, objects.updated_at, objects.created_at, objects.last_accessed_at, objects.metadata from files_folders
97+
)
98+
select files_folders.folder as name, objects.id, objects.updated_at, objects.created_at, objects.last_accessed_at, objects.metadata from files_folders
10399
left join objects
104-
on prefix || files_folders.folder = objects.name
105-
where objects.id is null or objects.bucket_id=_bucketId;
100+
on prefix || files_folders.folder = objects.name and objects.bucket_id=bucketname;
106101
END
107102
$function$;
108103

109104
GRANT ALL PRIVILEGES ON SCHEMA storage TO postgres;
110105
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA storage TO postgres;
111-
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA storage TO postgres;
106+
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA storage TO postgres;
107+
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
FROM supabase/storage-api:v0.35.1
1+
FROM supabase/storage-api:v1.8.2
22

3-
RUN apk add curl --no-cache
3+
RUN apk add curl --no-cache

packages/storage_client/lib/src/storage_file_api.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,14 +276,21 @@ class StorageFileApi {
276276
/// example `folder/image.png`.
277277
/// [toPath] is the new file path, including the new file name. For example
278278
/// `folder/image-new.png`.
279-
Future<String> move(String fromPath, String toPath) async {
279+
///
280+
/// When copying to a different bucket, you have to specify the [destinationBucket].
281+
Future<String> move(
282+
String fromPath,
283+
String toPath, {
284+
String? destinationBucket,
285+
}) async {
280286
final options = FetchOptions(headers: headers);
281287
final response = await _storageFetch.post(
282288
'$url/object/move',
283289
{
284290
'bucketId': bucketId,
285291
'sourceKey': fromPath,
286292
'destinationKey': toPath,
293+
if (destinationBucket != null) 'destinationBucket': destinationBucket,
287294
},
288295
options: options,
289296
);
@@ -297,14 +304,21 @@ class StorageFileApi {
297304
///
298305
/// [toPath] is the new file path, including the new file name. For example
299306
/// `folder/image-copy.png`.
300-
Future<String> copy(String fromPath, String toPath) async {
307+
///
308+
/// When copying to a different bucket, you have to specify the [destinationBucket].
309+
Future<String> copy(
310+
String fromPath,
311+
String toPath, {
312+
String? destinationBucket,
313+
}) async {
301314
final options = FetchOptions(headers: headers);
302315
final response = await _storageFetch.post(
303316
'$url/object/copy',
304317
{
305318
'bucketId': bucketId,
306319
'sourceKey': fromPath,
307320
'destinationKey': toPath,
321+
if (destinationBucket != null) 'destinationBucket': destinationBucket,
308322
},
309323
options: options,
310324
);

packages/storage_client/test/client_test.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,5 +388,51 @@ void main() {
388388

389389
await storage.from(newBucketName).copy(uploadPath, "$uploadPath 2");
390390
});
391+
392+
test('copy to different bucket', () async {
393+
final storage = SupabaseStorageClient(
394+
storageUrl, {'Authorization': 'Bearer $storageKey'});
395+
396+
try {
397+
await storage.from('bucket2').download(uploadPath);
398+
fail('File that does not exist was found');
399+
} on StorageException catch (error) {
400+
expect(error.error, 'not_found');
401+
}
402+
await storage
403+
.from(newBucketName)
404+
.copy(uploadPath, uploadPath, destinationBucket: 'bucket2');
405+
try {
406+
await storage.from('bucket2').download(uploadPath);
407+
} catch (error) {
408+
fail('File that was copied was not found');
409+
}
410+
});
411+
412+
test('move to different bucket', () async {
413+
final storage = SupabaseStorageClient(
414+
storageUrl, {'Authorization': 'Bearer $storageKey'});
415+
416+
try {
417+
await storage.from('bucket2').download('$uploadPath 3');
418+
fail('File that does not exist was found');
419+
} on StorageException catch (error) {
420+
expect(error.error, 'not_found');
421+
}
422+
await storage
423+
.from(newBucketName)
424+
.move(uploadPath, '$uploadPath 3', destinationBucket: 'bucket2');
425+
try {
426+
await storage.from('bucket2').download('$uploadPath 3');
427+
} catch (error) {
428+
fail('File that was moved was not found');
429+
}
430+
try {
431+
await storage.from(newBucketName).download(uploadPath);
432+
fail('File that was moved was found');
433+
} on StorageException catch (error) {
434+
expect(error.error, 'not_found');
435+
}
436+
});
391437
});
392438
}

0 commit comments

Comments
 (0)