diff --git a/packages/flutterfire_cli/lib/src/common/strings.dart b/packages/flutterfire_cli/lib/src/common/strings.dart index 5eb846e6..6309d4a7 100644 --- a/packages/flutterfire_cli/lib/src/common/strings.dart +++ b/packages/flutterfire_cli/lib/src/common/strings.dart @@ -103,6 +103,15 @@ const noPathVariableFound = r'There is no $PATH variable in your environment. ' const validationCheck = 'This should be validated before any configuration is written to the project.'; +/// Text appended to firebase tools CLI output even when successful. See: +/// https://github.com/invertase/flutterfire_cli/issues/262 +/// https://github.com/invertase/flutterfire_cli/issues/282 +const appendedErrorText = ''' +}{ + "status": "error", + "error": "Timed out." +}'''; + /// A base class for all FlutterFire CLI exceptions. abstract class FlutterFireException implements Exception {} diff --git a/packages/flutterfire_cli/lib/src/common/utils.dart b/packages/flutterfire_cli/lib/src/common/utils.dart index 15c76e7d..5c886e78 100644 --- a/packages/flutterfire_cli/lib/src/common/utils.dart +++ b/packages/flutterfire_cli/lib/src/common/utils.dart @@ -534,3 +534,10 @@ void validateAndroidPackageName(String appId) { ); } } + +String firebaseCLIJsonParse(String output) { + return output.replaceFirst( + appendedErrorText, + '}', + ); +} diff --git a/packages/flutterfire_cli/lib/src/firebase.dart b/packages/flutterfire_cli/lib/src/firebase.dart index ae641e72..afe2b484 100644 --- a/packages/flutterfire_cli/lib/src/firebase.dart +++ b/packages/flutterfire_cli/lib/src/firebase.dart @@ -97,7 +97,7 @@ Future> runFirebaseCommand( runInShell: true, ); - final jsonString = process.stdout.toString(); + final jsonString = firebaseCLIJsonParse(process.stdout.toString()); Map commandResult; diff --git a/packages/flutterfire_cli/test/unit_tests.dart b/packages/flutterfire_cli/test/unit_tests.dart index fb19df16..3d38382b 100644 --- a/packages/flutterfire_cli/test/unit_tests.dart +++ b/packages/flutterfire_cli/test/unit_tests.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutterfire_cli/src/common/strings.dart'; import 'package:flutterfire_cli/src/common/utils.dart'; import 'package:test/test.dart'; @@ -128,4 +130,96 @@ void main() { ); }); }); + + group( + 'Firebase CLI JSON response parser function `firebaseCLIJsonParse()`', + () { + test('Valid JSON response from Firebase CLI', () { + const jsonData = ''' + { + "status": "success", + "result": [ + { + "projectId": "project-id", + "projectNumber": "2380", + "displayName": "Display", + "name": "projects/project-id", + "resources": { + "hostingSite": "project-id", + "storageBucket": "project-id.appspot.com", + "locationId": "europe-west" + }, + "state": "ACTIVE", + "etag": "1_c74d64e0-ba66-42e6-88se-303ds3222dsds" + }, + { + "projectId": "rtc-test-94090", + "projectNumber": "978336444", + "displayName": "rtc-test", + "name": "projects/rtc-test-9848", + "resources": { + "hostingSite": "rtc-test-93d9839" + }, + "state": "ACTIVE", + "etag": "1_81c10fa0-f712-4cbf-b0fd-e35e0403094" + } + ] + } + '''; + + final jsonString = firebaseCLIJsonParse(jsonData); + // This should succeed in parsing JSON + final result = Map.from( + const JsonDecoder().convert(jsonString) as Map, + ); + + expect(result['status'], 'success'); + }); + + test('Invalid JSON response from Firebase CLI', () { + const jsonData = ''' + { + "status": "success", + "result": [ + { + "projectId": "project-id", + "projectNumber": "2380", + "displayName": "Display", + "name": "projects/project-id", + "resources": { + "hostingSite": "project-id", + "storageBucket": "project-id.appspot.com", + "locationId": "europe-west" + }, + "state": "ACTIVE", + "etag": "1_c74d64e0-ba66-42e6-88se-303ds3222dsds" + }, + { + "projectId": "rtc-test-94090", + "projectNumber": "978336444", + "displayName": "rtc-test", + "name": "projects/rtc-test-9848", + "resources": { + "hostingSite": "rtc-test-93d9839" + }, + "state": "ACTIVE", + "etag": "1_81c10fa0-f712-4cbf-b0fd-e35e0403094" + } + ] + }{ + "status": "error", + "error": "Timed out." + } + '''; + + final jsonString = firebaseCLIJsonParse(jsonData); + // This should succeed in parsing JSON + final result = Map.from( + const JsonDecoder().convert(jsonString) as Map, + ); + + expect(result['status'], 'success'); + }); + }, + ); }