invertase / flutterfire_cli

A CLI to help with using FlutterFire in your Flutter applications.
Apache License 2.0
172 stars 48 forks source link

[bug]: flutterfire configure "type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast" #328

Closed rainshen49 closed 2 months ago

rainshen49 commented 2 months ago

Is there an existing issue for this?

CLI Version

1.0.0

Firebase Tools version

13.13.2

Flutter Doctor Output

[✓] Flutter (Channel stable, 3.22.2, on IDX GNU/Linux 6.1.85+, locale en_US.UTF-8)
    • Flutter version 3.22.2 on channel stable at /home/user/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 761747bfc5 (7 weeks ago), 2024-06-05 22:15:13 +0200
    • Engine revision edd8546116
    • Dart version 3.4.3
    • DevTools version 2.34.3

[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /opt/android
    • Platform android-34, build-tools 30.0.3
    • ANDROID_SDK_ROOT = /opt/android
    • Java binary at: /usr/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.11+9-nixos)
    ! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses

[✗] Chrome - develop for the web (Cannot find Chrome executable at google-chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.

[✗] Linux toolchain - develop for Linux desktop
    ✗ clang++ is required for Linux development.
      It is likely available from your distribution (e.g.: apt install clang), or can be downloaded from https://releases.llvm.org/
    ✗ CMake is required for Linux development.
      It is likely available from your distribution (e.g.: apt install cmake), or can be downloaded from https://cmake.org/download/
    ✗ ninja is required for Linux development.
      It is likely available from your distribution (e.g.: apt install ninja-build), or can be downloaded from https://github.com/ninja-build/ninja/releases
    ✗ pkg-config is required for Linux development.
      It is likely available from your distribution (e.g.: apt install pkg-config), or can be downloaded from https://www.freedesktop.org/wiki/Software/pkg-config/

[!] Android Studio (not installed)
    • Android Studio not found; download from https://developer.android.com/studio/index.html
      (or visit https://flutter.dev/docs/get-started/install/linux#android-setup for detailed instructions).

[✓] Connected device (2 available)
    • Android SDK built for x86 (mobile) • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)
    • Linux (desktop)                    • linux         • linux-x64   • IDX GNU/Linux 6.1.85+

[✓] Network resources
    • All expected network resources are available.

! Doctor found issues in 4 categories.

Description

The CLI throws when running flutterfire configure on a blank workspace.

Steps to reproduce

Run flutterfire configure

Output:

⠦ Fetching available Firebase projects...                                                                                                                                                                                                           
type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast

Expected behavior

It should prompt me to pick a project.

Screenshots

No response

Additional context and comments

I have permission on a large number of internal projects. However, I'm unsure whether this is caused by too many projects to list because no additional debug info is available.

exaby73 commented 2 months ago

Hello @rainshen49. Thanks for raising this issue. Can you elaborate on what you mean by a blank workspace? I assumed an empty directory but if I run flutterfire configure in an empty directory, I get an expected error: FlutterAppRequiredException: The current directory does not appear to be a Flutter application project.

exaby73 commented 2 months ago

If you do suspect it's caused by a large number of projects, could you try running firebase projects:list directly to see if the underlying Firebase CLI isn't the cause of this issue? You could also try flutterfire configure --verbose to see if any additional logs are printed

rainshen49 commented 2 months ago

@exaby73 thanks for the quick response. I should be more specific. By "blank" workspace, I mean the initial state after running something like "flutter create blank_workspace".

Running firebase projects:list does not produce any errors.

...
5609 project(s) total.

Running flutterfire configure --verbose doesn't give me anything more than the non-verbose version.

flutterfire configure --verbose
⠧ Fetching available Firebase projects...                                                                                                                                                                                                           
type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast
rainshen49 commented 2 months ago

I'd like to add that I've also tried flutterfire configure --yes --project=<my project id>, but it still runs ⠏ Fetching available Firebase projects... so arrives at the same error.

exaby73 commented 2 months ago

Could you check of firebase projects:list --json has a null value anywhere in the list? We currently assume we're getting perfect data from the CLI and some unchecked casting happens (see link below). I guess is it's throwing here but unfortunately, I cannot reproduce so you'll have to help me track this down :)

https://github.com/invertase/flutterfire_cli/blob/add8df195e52a0ceee8bfbeb032205a30513e876/packages/flutterfire_cli/lib/src/firebase.dart#L126-L149

russellwheatley commented 2 months ago

Hey @rainshen49 - it might be down to the size of the JSON response. From my very rough calculations, it could be around 60000 lines if you have 5609 firebase projects.

We've had an issue before for FFCLI when there is a large amount of projects to parse: https://github.com/invertase/flutterfire_cli/issues/150

I'll try to get a fix up 👍

russellwheatley commented 2 months ago

@rainshen49 - do you mind cloning the flutterfire_cli project and testing this branch?

https://github.com/invertase/flutterfire_cli/tree/temp-file-for-large-json

To point your computer to the local version, run this command from the root of the project:

dart pub global activate --source="path" . --executable="flutterfire" --overwrite

In this branch, I've implemented a check to see whether the JSON response is large and used a temporary file to parse the data. I've also added logging which I hope will provide details on the problem.

When running FFCLI command, please add --debug flag for console output. Example:

flutterfire configure --yes --project=PROJECT_ID --debug

If you were interested in adding any further logging in the local flutterfire_cli, you need to delete the snapshot at this directory:

flutterfire_cli/.dart_tool/pub/bin/flutterfire_cli_monorepo/*.snapshot

Then run this command again so you're using the latest local flutterfire_cli version:

dart pub global activate --source="path" . --executable="flutterfire" --overwrite

Let me know how it goes 🙏

rainshen49 commented 2 months ago

@russellwheatley Thanks for the instruction, I was able to pinpoint where the null comes from. One of the projects has missing/empty resources field. This causes https://github.com/invertase/flutterfire_cli/blob/add8df195e52a0ceee8bfbeb032205a30513e876/packages/flutterfire_cli/lib/src/firebase/firebase_project.dart#L61 to throw. This situation is possible if the project has none of the "default" resources configured, that is, if they haven't enabled Hosting, Storage, Realtime Database, or Firestore (which initializes locationId). These resources used to be enabled by default with a Firebase project, but is no longer the case as of some time last year.

My recommendation here is to just provide an empty value of FirebaseProjectResources because the app may still function if it uses other products. I'm not familiar with dart, but this worked for me

resources: FirebaseProjectResources.fromJson(
    Map<String, dynamic>.from(
      json['resources'] ?? <String, dynamic>{} as Map,
    ),
  )
rainshen49 commented 2 months ago

I have some additional comment/suggestions since I browsed this part of the code already.

  1. firebase.getProjects seems to be called twice in a row for no reason https://github.com/invertase/flutterfire_cli/blob/add8df195e52a0ceee8bfbeb032205a30513e876/packages/flutterfire_cli/lib/src/commands/config.dart#L421-L434
  2. If a user already passed in a project, we could filter on the Map<String, dynamic> e before parsing https://github.com/invertase/flutterfire_cli/blob/main/packages/flutterfire_cli/lib/src/firebase.dart#L143. e.g.
      result
        .where(
          (Map<String, dynamic> e) =>
              selectedProject == null ||
              selectedProject == e['projectId'] ||
              selectedProject == e['projectNumber'],
        )
        .map<FirebaseProject>((Map<String, dynamic> e)

This way, if any other project is malformed and would fail to parse, we're good as long as the user's chosen project is well-formed. This approach should also be faster when the number of projects is large.

russellwheatley commented 2 months ago

@rainshen49 - ah, thanks for locating the bug and the additional getProjects(). I'll be sure to get these fixed and updated in the next release 🙏

rainshen49 commented 2 months ago

@russellwheatley Thanks! What's the usual release schedule?

russellwheatley commented 2 months ago

@rainshen49 - typically when there is a bug fix or new feature being released. I'll aim to get this fixed and a release cut this week 👍

russellwheatley commented 2 months ago

@rainshen49 - fix is in the latest dev release:

dart pub global activate flutterfire_cli 1.0.1-dev.3