diff --git a/README.md b/README.md index f2e62ba6d29db3b617f4d956a706dc82fabf3349..ecfa3ea1b289475209f93097313b6f84b95aea82 100644 --- a/README.md +++ b/README.md @@ -7,18 +7,18 @@ This is the _unofficial_ MinIO Dart Client SDK that provides simple APIs to acce ## API -| Bucket operations | Object operations | Presigned operations | Bucket Policy & Notification operations | -|-------------------------|--------------------------|----------------------|-----------------------------------------| -| [makeBucket] | [getObject] | [presignedUrl] | [getBucketNotification] | -| [listBuckets] | [getPartialObject] | [presignedGetObject] | [setBucketNotification] | -| [bucketExists] | [fGetObject] | [presignedPutObject] | [removeAllBucketNotification] | -| [removeBucket] | [putObject] | [presignedPostPolicy]| [listenBucketNotification] | -| [listObjects] | [fPutObject] | | [getBucketPolicy] | -| [listObjectsV2] | [copyObject] | | [setBucketPolicy] | -| [listIncompleteUploads] | [statObject] | | | -| | [removeObject] | | | -| | [removeObjects] | | | -| | [removeIncompleteUpload] | | | +| Bucket operations | Object operations | Presigned operations | Bucket Policy & Notification operations | +| ----------------------- | ------------------------ | --------------------- | --------------------------------------- | +| [makeBucket] | [getObject] | [presignedUrl] | [getBucketNotification] | +| [listBuckets] | [getPartialObject] | [presignedGetObject] | [setBucketNotification] | +| [bucketExists] | [fGetObject] | [presignedPutObject] | [removeAllBucketNotification] | +| [removeBucket] | [putObject] | [presignedPostPolicy] | [listenBucketNotification] | +| [listObjects] | [fPutObject] | | [getBucketPolicy] | +| [listObjectsV2] | [copyObject] | | [setBucketPolicy] | +| [listIncompleteUploads] | [statObject] | | | +| [listAllObjects] | [removeObject] | | | +| [listAllObjectsV2] | [removeObjects] | | | +| | [removeIncompleteUpload] | | | ## Usage @@ -45,6 +45,17 @@ final minio = Minio( ); ``` +**Filebase** + +```dart +final minio = Minio( + endPoint: 's3.filebase.com', + accessKey: 'YOUR-ACCESSKEYID', + secretKey: 'YOUR-SECRETACCESSKEY', + useSSL: true, +); +``` + **File upload** ```dart import 'package:minio/io.dart'; @@ -109,6 +120,8 @@ MIT [listObjects]: https://pub.dev/documentation/minio/latest/minio/Minio/listObjects.html [listObjectsV2]: https://pub.dev/documentation/minio/latest/minio/Minio/listObjectsV2.html [listIncompleteUploads]: https://pub.dev/documentation/minio/latest/minio/Minio/listIncompleteUploads.html +[listAllObjects]: https://pub.dev/documentation/minio/latest/minio/Minio/listAllObjects.html +[listAllObjectsV2]: https://pub.dev/documentation/minio/latest/minio/Minio/listAllObjectsV2.html [getObject]: https://pub.dev/documentation/minio/latest/minio/Minio/getObject.html [getPartialObject]: https://pub.dev/documentation/minio/latest/minio/Minio/getPartialObject.html diff --git a/example/filebase_example.dart b/example/filebase_example.dart new file mode 100644 index 0000000000000000000000000000000000000000..76b15201be5d91afbcbe532ebe4d0f35a6dab334 --- /dev/null +++ b/example/filebase_example.dart @@ -0,0 +1,16 @@ +import 'package:minio/minio.dart'; + +void main() async { + final minio = Minio( + endPoint: 's3.filebase.com', + accessKey: '<YOUR_ACCESS_KEY>', + secretKey: '<YOUR_SECRET_KEY>', + useSSL: true, + ); + + final buckets = await minio.listBuckets(); + print('buckets: $buckets'); + + final objects = await minio.listObjects(buckets.first.name).toList(); + print('objects: $objects'); +} diff --git a/lib/minio.dart b/lib/minio.dart index c4333be2a8b99daec627e4a6691be798cd1da822..34f8b0e0ed8f62f3af3645681fc83571fc9236fb 100644 --- a/lib/minio.dart +++ b/lib/minio.dart @@ -3,5 +3,3 @@ library minio; export 'src/minio.dart'; export 'src/minio_errors.dart'; export 'src/minio_stream.dart'; - -// TODO: Export any libraries intended for clients of this package. diff --git a/lib/src/minio_client.dart b/lib/src/minio_client.dart index 4287ce59c6685990c7dd1733b5be754f264c60ba..d4045b7ae84700d32ca25d21275b273afa509554 100644 --- a/lib/src/minio_client.dart +++ b/lib/src/minio_client.dart @@ -186,16 +186,21 @@ class MinioClient { if (object != null) path = '/$bucket/$object'; } - final resourcePart = resource == null ? '' : '$resource'; - final queryPart = queries == null ? '' : '&${encodeQueries(queries)}'; - final query = resourcePart + queryPart; + final query = StringBuffer(); + if (resource != null) { + query.write(resource); + } + if (queries != null) { + if (query.isNotEmpty) query.write('&'); + query.write(encodeQueries(queries)); + } return Uri( scheme: minio.useSSL ? 'https' : 'http', host: host, port: minio.port, pathSegments: path.split('/'), - query: query, + query: query.toString(), ); } diff --git a/lib/src/minio_models.dart b/lib/src/minio_models.dart index 801ca1d9a2181dd4e357d1c1435c9322c7663a30..f97abaeb7334e358fe1b64ff977161db71326dff 100644 --- a/lib/src/minio_models.dart +++ b/lib/src/minio_models.dart @@ -5,14 +5,22 @@ import 'package:xml/xml.dart'; import '../models.dart'; -class ListObjectsChunk { - ListObjectsChunk({ +class ListObjectsResult { + ListObjectsResult({ required this.objects, required this.prefixes, }); + /// Metadata about each object returned. final List<Object> objects; - final List<String?> prefixes; + + /// Like directorys in a file system, prefixes are delimited by slashes. + final List<String> prefixes; + + @override + String toString() { + return 'ListObjectsChunk{objects: $objects, prefixes: $prefixes}'; + } } class ListObjectsOutput { diff --git a/lib/src/minio_models_generated.dart b/lib/src/minio_models_generated.dart index 1e55235a0896efdfcde88f9c45439cc93a716f98..6c98d091f540f91e56cd4722648ccd0104283108 100644 --- a/lib/src/minio_models_generated.dart +++ b/lib/src/minio_models_generated.dart @@ -271,9 +271,11 @@ class Bucket { this.name, ); - Bucket.fromXml(XmlElement xml) { - creationDate = DateTime.parse(getProp(xml, 'CreationDate')!.text); - name = getProp(xml, 'Name')?.text; + factory Bucket.fromXml(XmlElement xml) { + return Bucket( + DateTime.parse(getProp(xml, 'CreationDate')!.text), + getProp(xml, 'Name')!.text, + ); } XmlNode toXml() { @@ -289,7 +291,12 @@ class Bucket { DateTime? creationDate; /// The name of the bucket. - String? name; + String name; + + @override + String toString() { + return 'Bucket{creationDate: $creationDate, name: $name}'; + } } /// Specifies the lifecycle configuration for objects in an Amazon S3 bucket. For more information, see Object Lifecycle Management in the Amazon Simple Storage Service Developer Guide. @@ -1091,6 +1098,11 @@ class Error { /// The version ID of the error. String? versionId; + + @override + String toString() { + return 'Error{code: $code, key: $key, message: $message, versionId: $versionId}'; + } } /// The error information. @@ -2270,6 +2282,11 @@ class Object { /// The class of storage used to store the object. String? storageClass; + + @override + String toString() { + return 'Object{eTag: $eTag, key: $key, lastModified: $lastModified, owner: $owner, size: $size, storageClass: $storageClass}'; + } } /// Object Identifier is unique value to identify objects. diff --git a/test/helpers.dart b/test/helpers.dart index 63ca1731a16c218e7f236ff6be5f3721977d131d..b0cd2d6a4b0f492f8cff0f6d625d4c565b8e81d2 100644 --- a/test/helpers.dart +++ b/test/helpers.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:minio/minio.dart'; /// Initializes an instance of [Minio] with per default valid configuration. @@ -24,3 +26,11 @@ Minio getMinioClient({ region: region, enableTrace: enableTrace, ); + +/// Generates a random name for a bucket or object. +String uniqueName() { + final random = Random(); + final now = DateTime.now(); + final name = 'id-${now.microsecondsSinceEpoch}-${random.nextInt(8192)}'; + return name; +} diff --git a/test/minio_stream_test.dart b/test/minio_stream_test.dart index 8c74cd832e9c86339a7769c785307f380f144d02..fbc031c2665dca77c60fe6c36502351220a0ca8b 100644 --- a/test/minio_stream_test.dart +++ b/test/minio_stream_test.dart @@ -4,7 +4,7 @@ import 'helpers.dart'; void main() { group('MinioByteStream', () { - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName = uniqueName(); final objectName = 'content-length-test'; final testData = [1, 2, 3, 4, 5]; diff --git a/test/minio_test.dart b/test/minio_test.dart index 3f00c45622b7159ea6e560ba4d5661026847f915..f31c88af943ce5bd9fe24d6793dcf1406a751a3d 100644 --- a/test/minio_test.dart +++ b/test/minio_test.dart @@ -77,10 +77,10 @@ void testListBuckets() { test('listBuckets() can list buckets', () async { final minio = getMinioClient(); - final bucketName1 = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName1 = uniqueName(); await minio.makeBucket(bucketName1); - final bucketName2 = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName2 = uniqueName(); await minio.makeBucket(bucketName2); final buckets = await minio.listBuckets(); @@ -97,10 +97,10 @@ void testListBuckets() { expect( () async => await minio.listBuckets(), throwsA( - isA<MinioError>().having( - (e) => e.message, + isA<MinioS3Error>().having( + (e) => e.error!.code, 'message', - 'The Access Key Id you provided does not exist in our records.', + 'AccessDenied', ), ), ); @@ -112,10 +112,10 @@ void testListBuckets() { expect( () async => await minio.listBuckets(), throwsA( - isA<MinioError>().having( - (e) => e.message, + isA<MinioS3Error>().having( + (e) => e.error!.code, 'message', - 'The request signature we calculated does not match the signature you provided. Check your key and signing method.', + 'AccessDenied', ), ), ); @@ -124,7 +124,7 @@ void testListBuckets() { void testBucketExists() { group('bucketExists', () { - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName = uniqueName(); setUpAll(() async { final minio = getMinioClient(); @@ -179,7 +179,7 @@ void testBucketExists() { void testFPutObject() { group('fPutObject', () { - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName = uniqueName(); late Directory tempDir; late File testFile; final objectName = 'a.jpg'; @@ -262,7 +262,7 @@ void testSetObjectACL() { final objectName = 'a.jpg'; setUpAll(() async { - bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + bucketName = uniqueName(); tempDir = await Directory.systemTemp.createTemp(); testFile = await File('${tempDir.path}/$objectName').create(); @@ -293,7 +293,7 @@ void testGetObjectACL() { final objectName = 'a.jpg'; setUpAll(() async { - bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + bucketName = uniqueName(); tempDir = await Directory.systemTemp.createTemp(); testFile = await File('${tempDir.path}/$objectName').create(); @@ -320,8 +320,8 @@ void testGetObjectACL() { void testGetObject() { group('getObject()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); - final objectName = DateTime.now().microsecondsSinceEpoch.toString(); + final bucketName = uniqueName(); + final objectName = uniqueName(); final objectData = Uint8List.fromList([1, 2, 3]); setUpAll(() async { @@ -361,7 +361,7 @@ void testGetObject() { void testPutObject() { group('putObject()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName = uniqueName(); final objectData = Uint8List.fromList([1, 2, 3]); setUpAll(() async { @@ -373,7 +373,7 @@ void testPutObject() { }); test('succeeds', () async { - final objectName = DateTime.now().microsecondsSinceEpoch.toString(); + final objectName = uniqueName(); await minio.putObject(bucketName, objectName, Stream.value(objectData)); final stat = await minio.statObject(bucketName, objectName); expect(stat.size, equals(objectData.length)); @@ -381,8 +381,7 @@ void testPutObject() { }); test('works with object names with symbols', () async { - final objectName = - DateTime.now().microsecondsSinceEpoch.toString() + r'-._~,!@#$%^&*()'; + final objectName = uniqueName() + r'-._~,!@#$%^&*()'; await minio.putObject(bucketName, objectName, Stream.value(objectData)); final stat = await minio.statObject(bucketName, objectName); expect(stat.size, equals(objectData.length)); @@ -394,7 +393,7 @@ void testPutObject() { void testGetBucketNotification() { group('getBucketNotification()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName = uniqueName(); setUpAll(() async { await minio.makeBucket(bucketName); @@ -413,7 +412,7 @@ void testGetBucketNotification() { void testSetBucketNotification() { group('setBucketNotification()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName = uniqueName(); setUpAll(() async { await minio.makeBucket(bucketName); @@ -435,7 +434,7 @@ void testSetBucketNotification() { void testRemoveAllBucketNotification() { group('removeAllBucketNotification()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName = uniqueName(); setUpAll(() async { await minio.makeBucket(bucketName); @@ -454,8 +453,8 @@ void testRemoveAllBucketNotification() { void testListenBucketNotification() { group('listenBucketNotification()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); - // final objectName = DateTime.now().microsecondsSinceEpoch.toString(); + final bucketName = uniqueName(); + // final objectName = uniqueName(); setUpAll(() async { await minio.makeBucket(bucketName); @@ -495,8 +494,8 @@ void testListenBucketNotification() { void testStatObject() { group('statObject()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); - final objectName = DateTime.now().microsecondsSinceEpoch.toString(); + final bucketName = uniqueName(); + final objectName = uniqueName(); final data = [1, 2, 3, 4, 5]; setUpAll(() async { @@ -536,7 +535,7 @@ void testStatObject() { void testMakeBucket() { group('makeBucket()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName = uniqueName(); setUpAll(() async { await minio.makeBucket(bucketName); @@ -557,7 +556,7 @@ void testMakeBucket() { void testRemoveBucket() { group('removeBucket()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); + final bucketName = uniqueName(); test('succeeds', () async { await minio.makeBucket(bucketName); @@ -576,8 +575,8 @@ void testRemoveBucket() { void testRemoveObject() { group('removeObject()', () { final minio = getMinioClient(); - final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); - final objectName = DateTime.now().microsecondsSinceEpoch.toString(); + final bucketName = uniqueName(); + final objectName = uniqueName(); final data = [1, 2, 3, 4, 5]; setUpAll(() async {