Skip to content

Commit 0115f79

Browse files
fix(database, web): fix broken exception handling on streams (#12647)
1 parent c899a7b commit 0115f79

File tree

3 files changed

+50
-13
lines changed

3 files changed

+50
-13
lines changed

packages/firebase_database/firebase_database_web/lib/src/interop/database.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,7 @@ class Query<T extends database_interop.QueryJsImpl> extends JsObjectWrapper<T> {
389389
});
390390

391391
final void Function(JSObject) cancelCallbackWrap = ((JSObject error) {
392-
final dartified = error.dartify();
393-
streamController
394-
.addError(convertFirebaseDatabaseException(dartified ?? {}));
392+
streamController.addError(error);
395393
streamController.close();
396394
});
397395

packages/firebase_database/firebase_database_web/lib/src/utils/exception.dart

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,25 @@ FirebaseException convertFirebaseDatabaseException(Object exception,
1010
[StackTrace? stackTrace]) {
1111
final castedJSObject = exception as core_interop.JSError;
1212
String code = 'unknown';
13-
String message = castedJSObject.message?.toDart ?? '';
13+
String message = castedJSObject.message?.toDart.toLowerCase() ?? '';
1414

1515
// FirebaseWeb SDK for Database has no error codes, so we manually map known
1616
// messages to known error codes for cross platform consistency.
17-
if (message.toLowerCase().contains('index not defined')) {
17+
if (message.contains('index not defined')) {
1818
code = 'index-not-defined';
19-
} else if (message.toLowerCase().contains('permission denied')) {
19+
} else if (message.contains('permission denied') ||
20+
message.contains('permission_denied')) {
2021
code = 'permission-denied';
2122
} else if (message
22-
.toLowerCase()
2323
.contains('transaction needs to be run again with current data')) {
2424
code = 'data-stale';
25-
} else if (message
26-
.toLowerCase()
27-
.contains('transaction had too many retries')) {
25+
} else if (message.contains('transaction had too many retries')) {
2826
code = 'max-retries';
29-
} else if (message.toLowerCase().contains('service is unavailable')) {
27+
} else if (message.contains('service is unavailable')) {
3028
code = 'unavailable';
31-
} else if (message.toLowerCase().contains('network error')) {
29+
} else if (message.contains('network error')) {
3230
code = 'network-error';
33-
} else if (message.toLowerCase().contains('write was canceled')) {
31+
} else if (message.contains('write was canceled')) {
3432
code = 'write-cancelled';
3533
}
3634

tests/integration_test/firebase_database/query_e2e.dart

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:async';
56
import 'package:collection/collection.dart';
7+
import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart';
68
import 'package:firebase_database/firebase_database.dart';
79
import 'package:flutter_test/flutter_test.dart';
810

@@ -499,5 +501,44 @@ void setupQueryTests() {
499501
retry: 2,
500502
);
501503
});
504+
505+
group('onValue', () {
506+
test('emits an event when the data changes', () async {
507+
await ref.set({
508+
'a': 2,
509+
'b': 3,
510+
'c': 1,
511+
});
512+
expect(
513+
ref.onValue,
514+
emitsInOrder([
515+
isA<DatabaseEvent>().having((s) => s.snapshot.value, 'value', {
516+
'a': 2,
517+
'b': 3,
518+
'c': 1,
519+
}).having((e) => e.type, 'type', DatabaseEventType.value),
520+
]),
521+
);
522+
});
523+
524+
test(
525+
'throw a `permission-denied` exception when accessing restricted data',
526+
() async {
527+
final Completer<FirebaseException> errorReceived =
528+
Completer<FirebaseException>();
529+
FirebaseDatabase.instance.ref().child('restricted').onValue.listen(
530+
(event) {
531+
// Do nothing
532+
},
533+
onError: (error) {
534+
errorReceived.complete(error);
535+
},
536+
);
537+
538+
final streamError = await errorReceived.future;
539+
expect(streamError, isA<FirebaseException>());
540+
expect(streamError.code, 'permission-denied');
541+
});
542+
});
502543
});
503544
}

0 commit comments

Comments
 (0)