Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,12 @@ private void sendEvent(String eventType, DataSnapshot snapshot, String previousC
}

@Override
public void onCancelled(DatabaseError error) {}
public void onCancelled(DatabaseError error) {
Map<String, Object> arguments = new HashMap<>();
arguments.put("handle", handle);
arguments.put("error", asMap(error));
channel.invokeMethod("Error", arguments);
}

@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
Expand Down Expand Up @@ -371,11 +376,7 @@ public void onComplete(
Map<String, Object> completionMap = new HashMap<>();
completionMap.put("transactionKey", arguments.get("transactionKey"));
if (databaseError != null) {
Map<String, Object> errorMap = new HashMap<>();
errorMap.put("code", databaseError.getCode());
errorMap.put("message", databaseError.getMessage());
errorMap.put("details", databaseError.getDetails());
completionMap.put("error", errorMap);
completionMap.put("error", asMap(databaseError));
}
completionMap.put("committed", committed);
if (dataSnapshot != null) {
Expand Down Expand Up @@ -445,4 +446,12 @@ public void onComplete(
}
}
}

private static Map<String, Object> asMap(DatabaseError error) {
Map<String, Object> map = new HashMap<>();
map.put("code", error.getCode());
map.put("message", error.getMessage());
map.put("details", error.getDetails());
return map;
}
}
21 changes: 16 additions & 5 deletions packages/firebase_database/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class _MyHomePageState extends State<MyHomePage> {

String _kTestKey = 'Hello';
String _kTestValue = 'world!';
DatabaseError _error;

@override
void initState() {
Expand All @@ -49,12 +50,19 @@ class _MyHomePageState extends State<MyHomePage> {
_counterRef.keepSynced(true);
_counterSubscription = _counterRef.onValue.listen((Event event) {
setState(() {
_error = null;
_counter = event.snapshot.value ?? 0;
});
}, onError: (DatabaseError error) {
setState(() {
_error = error;
});
});
_messagesSubscription =
_messagesRef.limitToLast(10).onChildAdded.listen((Event event) {
print('Child added: ${event.snapshot.value}');
}, onError: (DatabaseError error) {
print('Error: ${error.code} ${error.message}');
});
}

Expand Down Expand Up @@ -96,11 +104,14 @@ class _MyHomePageState extends State<MyHomePage> {
children: <Widget>[
new Flexible(
child: new Center(
// ignore: prefer_const_constructors
child: new Text(
'Button tapped $_counter time${ _counter == 1 ? '' : 's' }.\n\n'
'This includes all devices, ever.',
),
child: _error == null
? new Text(
'Button tapped $_counter time${ _counter == 1 ? '' : 's' }.\n\n'
'This includes all devices, ever.',
)
: new Text(
'Error retrieving button tap count:\n${_error.message}',
),
),
),
new ListTile(
Expand Down
21 changes: 18 additions & 3 deletions packages/firebase_database/ios/Classes/FirebaseDatabasePlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ @interface FLTFirebaseDatabasePlugin ()

@interface NSError (FlutterError)
@property(readonly, nonatomic) FlutterError *flutterError;
@property(readonly, nonatomic) NSDictionary *dictionary;
@end

@implementation NSError (FlutterError)
Expand All @@ -19,6 +20,14 @@ - (FlutterError *)flutterError {
message:self.domain
details:self.localizedDescription];
}

- (NSDictionary *)dictionary {
return @{
@"code" : @(self.code),
@"message" : self.domain ?: [NSNull null],
@"details" : self.localizedDescription ?: [NSNull null],
};
}
@end

FIRDatabaseReference *getReference(NSDictionary *arguments) {
Expand Down Expand Up @@ -252,15 +261,14 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
// Invoke transaction completion on the Dart side.
result(@{
@"transactionKey" : call.arguments[@"transactionKey"],
@"error" : error.flutterError ?: [NSNull null],
@"error" : error.dictionary ?: [NSNull null],
@"committed" : [NSNumber numberWithBool:committed],
@"snapshot" : @{@"key" : snapshot.key ?: [NSNull null], @"value" : snapshot.value}
});
}];
} else if ([@"Query#observe" isEqualToString:call.method]) {
FIRDataEventType eventType = parseEventType(call.arguments[@"eventType"]);
__block FIRDatabaseHandle handle = [getQuery(call.arguments)
observeEventType:eventType
__block FIRDatabaseHandle handle = [getQuery(call.arguments) observeEventType:eventType
andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *snapshot, NSString *previousSiblingKey) {
[self.channel invokeMethod:@"Event"
arguments:@{
Expand All @@ -271,6 +279,13 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
},
@"previousSiblingKey" : previousSiblingKey ?: [NSNull null],
}];
}
withCancelBlock:^(NSError *error) {
[self.channel invokeMethod:@"Error"
arguments:@{
@"handle" : [NSNumber numberWithUnsignedInteger:handle],
@"error" : error.dictionary,
}];
}];
result([NSNumber numberWithUnsignedInteger:handle]);
} else if ([@"Query#removeObserver" isEqualToString:call.method]) {
Expand Down
33 changes: 20 additions & 13 deletions packages/firebase_database/lib/src/firebase_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,26 @@ class FirebaseDatabase {

FirebaseDatabase._() {
_channel.setMethodCallHandler((MethodCall call) async {
if (call.method == 'Event') {
final Event event = new Event._(call.arguments);
_observers[call.arguments['handle']].add(event);
} else if (call.method == 'DoTransaction') {
final MutableData mutableData =
new MutableData.private(call.arguments['snapshot']);
final MutableData updated =
await _transactions[call.arguments['transactionKey']](mutableData);
return <String, dynamic>{'value': updated.value};
} else {
throw new MissingPluginException(
'${call.method} method not implemented on the Dart side.',
);
switch (call.method) {
case 'Event':
final Event event = new Event._(call.arguments);
_observers[call.arguments['handle']].add(event);
return null;
case 'Error':
final DatabaseError error =
new DatabaseError._(call.arguments['error']);
_observers[call.arguments['handle']].addError(error);
return null;
case 'DoTransaction':
final MutableData mutableData =
new MutableData.private(call.arguments['snapshot']);
final MutableData updated = await _transactions[
call.arguments['transactionKey']](mutableData);
return <String, dynamic>{'value': updated.value};
default:
throw new MissingPluginException(
'${call.method} method not implemented on the Dart side.',
);
}
});
}
Expand Down
22 changes: 16 additions & 6 deletions packages/firebase_database/lib/ui/firebase_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import 'dart:collection';

import 'package:meta/meta.dart';

import '../firebase_database.dart' show DataSnapshot, Event, Query;
import '../firebase_database.dart'
show DatabaseError, DataSnapshot, Event, Query;
import 'utils/stream_subscriber_mixin.dart';

typedef void ChildCallback(int index, DataSnapshot snapshot);
typedef void ChildMovedCallback(
int fromIndex, int toIndex, DataSnapshot snapshot);
typedef void ValueCallback(DataSnapshot snapshot);
typedef void ErrorCallback(DatabaseError error);

/// Sorts the results of `query` on the client side using `DataSnapshot.key`.
class FirebaseList extends ListBase<DataSnapshot>
Expand All @@ -24,13 +26,14 @@ class FirebaseList extends ListBase<DataSnapshot>
this.onChildChanged,
this.onChildMoved,
this.onValue,
this.onError,
}) {
assert(query != null);
listen(query.onChildAdded, _onChildAdded);
listen(query.onChildRemoved, _onChildRemoved);
listen(query.onChildChanged, _onChildChanged);
listen(query.onChildMoved, _onChildMoved);
listen(query.onValue, _onValue);
listen(query.onChildAdded, _onChildAdded, onError: _onError);
listen(query.onChildRemoved, _onChildRemoved, onError: _onError);
listen(query.onChildChanged, _onChildChanged, onError: _onError);
listen(query.onChildMoved, _onChildMoved, onError: _onError);
listen(query.onValue, _onValue, onError: _onError);
}

/// Database query used to populate the list
Expand All @@ -51,6 +54,9 @@ class FirebaseList extends ListBase<DataSnapshot>
/// Called when the data of the list has finished loading
final ValueCallback onValue;

/// Called when an error is reported (e.g. permission denied)
final ErrorCallback onError;

// ListBase implementation
final List<DataSnapshot> _snapshots = <DataSnapshot>[];

Expand Down Expand Up @@ -127,4 +133,8 @@ class FirebaseList extends ListBase<DataSnapshot>
void _onValue(Event event) {
onValue(event.snapshot);
}

void _onError(DatabaseError error) {
onError?.call(error);
}
}
21 changes: 15 additions & 6 deletions packages/firebase_database/lib/ui/firebase_sorted_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import 'dart:collection';

import 'package:meta/meta.dart';

import '../firebase_database.dart' show DataSnapshot, Event, Query;
import 'firebase_list.dart' show ChildCallback, ValueCallback;
import '../firebase_database.dart'
show DatabaseError, DataSnapshot, Event, Query;
import 'firebase_list.dart' show ChildCallback, ErrorCallback, ValueCallback;
import 'utils/stream_subscriber_mixin.dart';

/// Sorts the results of `query` on the client side using to the `comparator`.
Expand All @@ -26,13 +27,14 @@ class FirebaseSortedList extends ListBase<DataSnapshot>
this.onChildRemoved,
this.onChildChanged,
this.onValue,
this.onError,
}) {
assert(query != null);
assert(comparator != null);
listen(query.onChildAdded, _onChildAdded);
listen(query.onChildRemoved, _onChildRemoved);
listen(query.onChildChanged, _onChildChanged);
listen(query.onValue, _onValue);
listen(query.onChildAdded, _onChildAdded, onError: _onError);
listen(query.onChildRemoved, _onChildRemoved, onError: _onError);
listen(query.onChildChanged, _onChildChanged, onError: _onError);
listen(query.onValue, _onValue, onError: _onError);
}

/// Database query used to populate the list
Expand All @@ -53,6 +55,9 @@ class FirebaseSortedList extends ListBase<DataSnapshot>
/// Called when the data of the list has finished loading
final ValueCallback onValue;

/// Called when an error is reported (e.g. permission denied)
final ErrorCallback onError;

// ListBase implementation
final List<DataSnapshot> _snapshots = <DataSnapshot>[];

Expand Down Expand Up @@ -108,4 +113,8 @@ class FirebaseSortedList extends ListBase<DataSnapshot>
void _onValue(Event event) {
onValue(event.snapshot);
}

void _onError(DatabaseError error) {
onError?.call(error);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ abstract class StreamSubscriberMixin<T> {
List<StreamSubscription<T>> _subscriptions = <StreamSubscription<T>>[];

/// Listens to a stream and saves it to the list of subscriptions.
void listen(Stream<T> stream, void onData(T data)) {
void listen(Stream<T> stream, void onData(T data), {Function onError}) {
if (stream != null) {
_subscriptions.add(stream.listen(onData));
_subscriptions.add(stream.listen(onData, onError: onError));
}
}

Expand Down
42 changes: 42 additions & 0 deletions packages/firebase_database/test/firebase_database_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,48 @@ void main() {
],
);
});
test('observing error events', () async {
mockHandleId = 99;
const int errorCode = 12;
const String errorDetails = 'Some details';
final Query query = database.reference().child('some path');
Future<Null> simulateError(String errorMessage) async {
await BinaryMessages.handlePlatformMessage(
channel.name,
channel.codec.encodeMethodCall(
new MethodCall('Error', <String, dynamic>{
'handle': 99,
'error': <String, dynamic>{
'code': errorCode,
'message': errorMessage,
'details': errorDetails,
},
}),
),
(_) {},
);
}

final AsyncQueue<DatabaseError> errors =
new AsyncQueue<DatabaseError>();

// Subscribe and allow subscription to complete.
final StreamSubscription<Event> subscription =
query.onValue.listen((_) {}, onError: errors.add);
await new Future<Null>.delayed(const Duration(seconds: 0));

await simulateError('Bad foo');
await simulateError('Bad bar');
final DatabaseError error1 = await errors.remove();
final DatabaseError error2 = await errors.remove();
subscription.cancel();
expect(error1.code, errorCode);
expect(error1.message, 'Bad foo');
expect(error1.details, errorDetails);
expect(error2.code, errorCode);
expect(error2.message, 'Bad bar');
expect(error2.details, errorDetails);
});
test('observing value events', () async {
mockHandleId = 87;
final String path = 'foo';
Expand Down