Add timeout properties to FirebaseStorage (#532)
diff --git a/packages/firebase_storage/android/src/main/java/io/flutter/plugins/firebase/storage/FirebaseStoragePlugin.java b/packages/firebase_storage/android/src/main/java/io/flutter/plugins/firebase/storage/FirebaseStoragePlugin.java index 46318d9..06296fd 100755 --- a/packages/firebase_storage/android/src/main/java/io/flutter/plugins/firebase/storage/FirebaseStoragePlugin.java +++ b/packages/firebase_storage/android/src/main/java/io/flutter/plugins/firebase/storage/FirebaseStoragePlugin.java
@@ -53,6 +53,24 @@ } switch (call.method) { + case "FirebaseStorage#getMaxDownloadRetryTime": + result.success(firebaseStorage.getMaxDownloadRetryTimeMillis()); + break; + case "FirebaseStorage#getMaxUploadRetryTime": + result.success(firebaseStorage.getMaxUploadRetryTimeMillis()); + break; + case "FirebaseStorage#getMaxOperationRetryTime": + result.success(firebaseStorage.getMaxOperationRetryTimeMillis()); + break; + case "FirebaseStorage#setMaxDownloadRetryTime": + setMaxDownloadRetryTimeMillis(call, result); + break; + case "FirebaseStorage#setMaxUploadRetryTime": + setMaxUploadRetryTimeMillis(call, result); + break; + case "FirebaseStorage#setMaxOperationRetryTime": + setMaxOperationTimeMillis(call, result); + break; case "StorageReference#putFile": putFile(call, result); break; @@ -92,6 +110,24 @@ } } + private void setMaxDownloadRetryTimeMillis(MethodCall call, Result result) { + Number time = call.argument("time"); + firebaseStorage.setMaxDownloadRetryTimeMillis(time.longValue()); + result.success(null); + } + + private void setMaxUploadRetryTimeMillis(MethodCall call, Result result) { + Number time = call.argument("time"); + firebaseStorage.setMaxUploadRetryTimeMillis(time.longValue()); + result.success(null); + } + + private void setMaxOperationTimeMillis(MethodCall call, Result result) { + Number time = call.argument("time"); + firebaseStorage.setMaxOperationRetryTimeMillis(time.longValue()); + result.success(null); + } + private void getMetadata(MethodCall call, final Result result) { String path = call.argument("path"); StorageReference ref = firebaseStorage.getReference().child(path);
diff --git a/packages/firebase_storage/ios/Classes/FirebaseStoragePlugin.m b/packages/firebase_storage/ios/Classes/FirebaseStoragePlugin.m index 1e20e59..7a889a2 100644 --- a/packages/firebase_storage/ios/Classes/FirebaseStoragePlugin.m +++ b/packages/firebase_storage/ios/Classes/FirebaseStoragePlugin.m
@@ -19,6 +19,8 @@ @end @implementation FLTFirebaseStoragePlugin { + NSMutableDictionary<NSString * /* app name */, + NSMutableDictionary<NSString * /* bucket */, FIRStorage *> *> *_storageMap; FIRStorage *storage; } @@ -36,24 +38,26 @@ if (![FIRApp defaultApp]) { [FIRApp configure]; } + _storageMap = [[NSMutableDictionary alloc] init]; } return self; } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *appName = call.arguments[@"app"]; - NSString *storageBucket = call.arguments[@"bucket"]; - if ([appName isEqual:[NSNull null]] && [storageBucket isEqual:[NSNull null]]) { - storage = [FIRStorage storage]; - } else if ([appName isEqual:[NSNull null]]) { - storage = [FIRStorage storageWithURL:storageBucket]; - } else if ([storageBucket isEqual:[NSNull null]]) { - storage = [FIRStorage storageForApp:[FIRApp appNamed:appName]]; - } else { - storage = [FIRStorage storageForApp:[FIRApp appNamed:appName] URL:storageBucket]; - } - - if ([@"StorageReference#putFile" isEqualToString:call.method]) { + storage = [self getStorage:call result:result]; + if ([@"FirebaseStorage#getMaxDownloadRetryTime" isEqualToString:call.method]) { + result(@((int64_t)(storage.maxDownloadRetryTime * 1000.0))); + } else if ([@"FirebaseStorage#getMaxUploadRetryTime" isEqualToString:call.method]) { + result(@((int64_t)(storage.maxUploadRetryTime * 1000.0))); + } else if ([@"FirebaseStorage#getMaxOperationRetryTime" isEqualToString:call.method]) { + result(@((int64_t)(storage.maxOperationRetryTime * 1000.0))); + } else if ([@"FirebaseStorage#setMaxDownloadRetryTime" isEqualToString:call.method]) { + [self setMaxDownloadRetryTime:call result:result]; + } else if ([@"FirebaseStorage#setMaxUploadRetryTime" isEqualToString:call.method]) { + [self setMaxUploadRetryTime:call result:result]; + } else if ([@"FirebaseStorage#setMaxOperationRetryTime" isEqualToString:call.method]) { + [self setMaxOperationRetryTime:call result:result]; + } else if ([@"StorageReference#putFile" isEqualToString:call.method]) { [self putFile:call result:result]; } else if ([@"StorageReference#putData" isEqualToString:call.method]) { [self putData:call result:result]; @@ -80,6 +84,83 @@ } } +// Returns a [FIRStorage] instance which is a singleton given a fixed app and bucket. +// This is to be consistent with the Android API so that repated calls to getters/setters +// affect the right [FIRStorage] instance. +- (FIRStorage *)getStorage:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *appName = call.arguments[@"app"]; + NSString *bucketUrl = call.arguments[@"bucket"]; + FIRApp *app; + + if ([appName isEqual:[NSNull null]]) { + app = [FIRApp defaultApp]; + } else { + app = [FIRApp appNamed:appName]; + } + + if ([bucketUrl isEqual:[NSNull null]]) { + if (app.options.storageBucket) { + bucketUrl = [app.options.storageBucket isEqualToString:@""] + ? @"" + : [@"gs://" stringByAppendingString:app.options.storageBucket]; + } else { + bucketUrl = nil; + } + } + + NSURL *url = [NSURL URLWithString:bucketUrl]; + if (!url) { + @try { + // Call storage constructor to raise proper exception. + storage = [FIRStorage storageForApp:app URL:bucketUrl]; + } @catch (NSException *exception) { + result([FlutterError errorWithCode:@"storage_error" + message:[exception name] + details:[exception reason]]); + } + } + + NSMutableDictionary *bucketMap = _storageMap[app.name]; + if (!bucketMap) { + bucketMap = [NSMutableDictionary dictionaryWithCapacity:1]; + _storageMap[app.name] = bucketMap; + } + + NSString *bucketName = [url host]; + FIRStorage *storage = bucketMap[bucketName]; + if (!storage) { + // Raises an exception if bucketUrl is invalid. + @try { + storage = [FIRStorage storageForApp:app URL:bucketUrl]; + } @catch (NSException *exception) { + result([FlutterError errorWithCode:@"storage_error" + message:[exception name] + details:[exception reason]]); + } + bucketMap[bucketName] = storage; + } + + return storage; +} + +- (void)setMaxDownloadRetryTime:(FlutterMethodCall *)call result:(FlutterResult)result { + NSNumber *time = call.arguments[@"time"]; + storage.maxDownloadRetryTime = [time longLongValue] / 1000.0; + result(nil); +} + +- (void)setMaxUploadRetryTime:(FlutterMethodCall *)call result:(FlutterResult)result { + NSNumber *time = call.arguments[@"time"]; + storage.maxUploadRetryTime = [time longLongValue] / 1000.0; + result(nil); +} + +- (void)setMaxOperationRetryTime:(FlutterMethodCall *)call result:(FlutterResult)result { + NSNumber *time = call.arguments[@"time"]; + storage.maxOperationRetryTime = [time longLongValue] / 1000.0; + result(nil); +} + - (void)putFile:(FlutterMethodCall *)call result:(FlutterResult)result { NSData *data = [NSData dataWithContentsOfFile:call.arguments[@"filename"]]; [self put:data call:call result:result];
diff --git a/packages/firebase_storage/lib/firebase_storage.dart b/packages/firebase_storage/lib/firebase_storage.dart index 82fca5f..726debc 100755 --- a/packages/firebase_storage/lib/firebase_storage.dart +++ b/packages/firebase_storage/lib/firebase_storage.dart
@@ -17,7 +17,11 @@ /// Returns the [FirebaseStorage] instance, initialized with a custom /// [FirebaseApp] if [app] is specified and a custom Google Cloud Storage - /// bucket if [storageBucket] is specified. + /// bucket if [storageBucket] is specified. Otherwise the instance will be + /// initialized with the default [FirebaseApp]. + /// + /// The [FirebaseStorage] instance is a singleton for fixed [app] and + /// [storageBucket]. /// /// The [storageBucket] argument is the gs:// url to the custom Firebase /// Storage Bucket. @@ -44,6 +48,57 @@ /// Creates a new [StorageReference] initialized at the root /// Firebase Storage location. StorageReference ref() => new StorageReference._(const <String>[], this); + + Future<int> getMaxDownloadRetryTimeMillis() async { + return await channel.invokeMethod( + "FirebaseStorage#getMaxDownloadRetryTime", <String, dynamic>{ + 'app': app?.name, + 'bucket': storageBucket, + }); + } + + Future<int> getMaxUploadRetryTimeMillis() async { + return await channel.invokeMethod( + "FirebaseStorage#getMaxUploadRetryTime", <String, dynamic>{ + 'app': app?.name, + 'bucket': storageBucket, + }); + } + + Future<int> getMaxOperationRetryTimeMillis() async { + return await channel.invokeMethod( + "FirebaseStorage#getMaxOperationRetryTime", <String, dynamic>{ + 'app': app?.name, + 'bucket': storageBucket, + }); + } + + Future<void> setMaxDownloadRetryTimeMillis(int time) { + return channel.invokeMethod( + "FirebaseStorage#setMaxDownloadRetryTime", <String, dynamic>{ + 'app': app?.name, + 'bucket': storageBucket, + 'time': time, + }); + } + + Future<void> setMaxUploadRetryTimeMillis(int time) { + return channel.invokeMethod( + "FirebaseStorage#setMaxUploadRetryTime", <String, dynamic>{ + 'app': app?.name, + 'bucket': storageBucket, + 'time': time, + }); + } + + Future<void> setMaxOperationRetryTimeMillis(int time) { + return channel.invokeMethod( + "FirebaseStorage#setMaxOperationRetryTime", <String, dynamic>{ + 'app': app?.name, + 'bucket': storageBucket, + 'time': time, + }); + } } class StorageReference {
diff --git a/packages/firebase_storage/test/firebase_storage_test.dart b/packages/firebase_storage/test/firebase_storage_test.dart index 1697821..667352e 100644 --- a/packages/firebase_storage/test/firebase_storage_test.dart +++ b/packages/firebase_storage/test/firebase_storage_test.dart
@@ -19,6 +19,174 @@ final FirebaseStorage storage = new FirebaseStorage(app: app, storageBucket: storageBucket); + group('getMaxDownloadRetryTimeMillis', () { + final List<MethodCall> log = <MethodCall>[]; + + setUp(() { + FirebaseStorage.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return 1000; + }); + }); + + test('invokes correct method', () async { + await storage.getMaxDownloadRetryTimeMillis(); + + expect(log, <Matcher>[ + isMethodCall( + 'FirebaseStorage#getMaxDownloadRetryTime', + arguments: <String, String>{ + 'app': 'testApp', + 'bucket': 'gs://fake-storage-bucket-url.com', + }, + ), + ]); + }); + + test('returns correct result', () async { + expect(await storage.getMaxDownloadRetryTimeMillis(), 1000); + }); + }); + + group('getMaxUploadRetryTimeMillis', () { + final List<MethodCall> log = <MethodCall>[]; + + setUp(() { + FirebaseStorage.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return 2000; + }); + }); + + test('invokes correct method', () async { + await storage.getMaxUploadRetryTimeMillis(); + + expect(log, <Matcher>[ + isMethodCall( + 'FirebaseStorage#getMaxUploadRetryTime', + arguments: <String, String>{ + 'app': 'testApp', + 'bucket': 'gs://fake-storage-bucket-url.com', + }, + ), + ]); + }); + + test('returns correct result', () async { + expect(await storage.getMaxUploadRetryTimeMillis(), 2000); + }); + }); + + group('getMaxOperationRetryTimeMillis', () { + final List<MethodCall> log = <MethodCall>[]; + + setUp(() { + FirebaseStorage.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return 3000; + }); + }); + + test('invokes correct method', () async { + await storage.getMaxOperationRetryTimeMillis(); + + expect(log, <Matcher>[ + isMethodCall( + 'FirebaseStorage#getMaxOperationRetryTime', + arguments: <String, String>{ + 'app': 'testApp', + 'bucket': 'gs://fake-storage-bucket-url.com', + }, + ), + ]); + }); + + test('returns correct result', () async { + expect(await storage.getMaxOperationRetryTimeMillis(), 3000); + }); + }); + + group('setMaxDownloadRetryTimeMillis', () { + final List<MethodCall> log = <MethodCall>[]; + + setUp(() { + FirebaseStorage.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + }); + }); + + test('invokes correct method', () async { + await storage.setMaxDownloadRetryTimeMillis(1000); + + expect(log, <Matcher>[ + isMethodCall( + 'FirebaseStorage#setMaxDownloadRetryTime', + arguments: <String, dynamic>{ + 'app': 'testApp', + 'bucket': 'gs://fake-storage-bucket-url.com', + 'time': 1000, + }, + ), + ]); + }); + }); + + group('setMaxUploadRetryTimeMillis', () { + final List<MethodCall> log = <MethodCall>[]; + + setUp(() { + FirebaseStorage.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + }); + }); + + test('invokes correct method', () async { + await storage.setMaxUploadRetryTimeMillis(2000); + + expect(log, <Matcher>[ + isMethodCall( + 'FirebaseStorage#setMaxUploadRetryTime', + arguments: <String, dynamic>{ + 'app': 'testApp', + 'bucket': 'gs://fake-storage-bucket-url.com', + 'time': 2000, + }, + ), + ]); + }); + }); + + group('setMaxOperationRetryTimeMillis', () { + final List<MethodCall> log = <MethodCall>[]; + + setUp(() { + FirebaseStorage.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + }); + }); + + test('invokes correct method', () async { + await storage.setMaxOperationRetryTimeMillis(3000); + + expect(log, <Matcher>[ + isMethodCall( + 'FirebaseStorage#setMaxOperationRetryTime', + arguments: <String, dynamic>{ + 'app': 'testApp', + 'bucket': 'gs://fake-storage-bucket-url.com', + 'time': 3000, + }, + ), + ]); + }); + }); + group('StorageReference', () { group('getData', () { final List<MethodCall> log = <MethodCall>[];