firebase / flutterfire

🔥 A collection of Firebase plugins for Flutter apps.
https://firebase.google.com/docs/flutter/setup
BSD 3-Clause "New" or "Revised" License
8.5k stars 3.92k forks source link

[cloud_functions]: No way to define headers like 'Content-Type': 'application/json', to send a POST request #12934

Open uhbsksingh opened 2 weeks ago

uhbsksingh commented 2 weeks ago

Is there an existing issue for this?

Which plugins are affected?

Cloud Functions

Which platforms are affected?

iOS

Description

I cannot figure out what is causing the issue. I think it has something to do with sending a POST request with header

'Content-Type': 'application/json'

The cloud function works as expected when sending a POST request from Postman. I sent the parameters in the request body. However, requesting from flutter using await callable.call throws an exception which is not informative. Is there any other way to make such a request?

My log output from

on FirebaseFunctionsException catch (error) {
      print("error.code: ${error.code}");
      print("error.details: ${error.details}");
      print("error.message: ${error.message}");
    }

is

flutter: error.code: internal
flutter: error.details: null
flutter: error.message: INTERNAL

Reproducing the issue

This is my flutter/dart code to call the cloud function.

try {
      final HttpsCallable callable =
          FirebaseFunctions.instanceFor(region: 'us-central1').httpsCallable('addAllocatedPurchase');

      final response = await callable.call(<String, dynamic>{
        'data': {
          'activeCollectionId': activeCollectionId,
          'action': action.toJson(),
          'purchaseBillList': purchaseBillList
              .map((e) => <String, dynamic>{
                    'billID': e.billID,
                    'reference': e.reference,
                    'partner': e.partner.toJson(),
                    'info': {
                      'billDateTime': e.info.billDateTime.toIso8601String(),
                      'dueDateTime': e.info.dueDateTime.toIso8601String(),
                      'dateTime': e.info.dateTime.toIso8601String(),
                      'currency': e.info.currency,
                      'action': e.info.action.toJson(),
                      'paymentTerm': e.info.paymentTerm?.toJson(),
                      'attachments': e.info.attachments,
                      'tags': e.info.tags,
                    },
                    'item': e.item.toJson(),
                    'quantity': e.quantity,
                    'rate': e.rate,
                    'tax': e.tax?.map((k, v) => MapEntry(k, v.toJson())),
                  })
              .toList(),
          'transferEntityDocumentID': transferEntity.documentID,
          'appliedQuantity': appliedQuantity,
          'billItemImageMap': billItemImageMap,
        }
      });
      print(response.data);
    } on FirebaseFunctionsException catch (error) {
      print("error.code: ${error.code}");
      print("error.details: ${error.details}");
      print("error.message: ${error.message}");
    } catch (exception) {
      print('Error: ${exception.toString()}');
      throw exception;
    }

And this is my deployed cloud function

import admin = require("firebase-admin");

interface AllocatedPurchaseDataModel {
    activeCollectionId: string;
    action: string;
    purchaseBillList: AllocatedPurchaseBillDataModel[];
    transferEntityDocumentID:string;
    appliedQuantity: number;
    billItemImageMap: { [key: string]: string[] };
}

export const addAllocatedPurchase =
  onCall<AllocatedPurchaseDataModel, Promise<FunctionResponse>>(
    async ( request: CallableRequest<AllocatedPurchaseDataModel>) => {
      const {
        activeCollectionId,
        action,
        purchaseBillList,
        transferEntityDocumentID,
        appliedQuantity,
        billItemImageMap,
      } = request.data;
      admin.initializeApp();
      const firestore : Firestore= admin.firestore();
      const batch :WriteBatch= firestore.batch();

      try {
        .......
      } catch (exception) {
        throw new HttpsError("unknown", "An error occurred", exception);
      }
    });

Firebase Core version

2.24.2

Flutter Version

3.19.4

Relevant Log Output

No response

Flutter dependencies

Expand Flutter dependencies snippet
```yaml Dart SDK 3.3.2 Flutter SDK 3.19.4 easyims_repository 0.0.0 dependencies: - cache 1.0.0 - cloud_firestore 4.13.6 [cloud_firestore_platform_interface cloud_firestore_web collection firebase_core firebase_core_platform_interface flutter meta] - cloud_functions 4.7.6 [cloud_functions_platform_interface cloud_functions_web firebase_core firebase_core_platform_interface flutter] - const_date_time 1.1.0 - equatable 2.0.5 [collection meta] - excel 4.0.3 [archive xml collection equatable] - firebase_core 2.32.0 [firebase_core_platform_interface firebase_core_web flutter meta] - firebase_storage 11.5.6 [firebase_core firebase_core_platform_interface firebase_storage_platform_interface firebase_storage_web flutter] - flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine] - json_annotation 4.8.1 [meta] dev dependencies: - build_runner 2.4.7 [analyzer args async build build_config build_daemon build_resolvers build_runner_core code_builder collection crypto dart_style frontend_server_client glob graphs http_multi_server io js logging meta mime package_config path pool pub_semver pubspec_parse shelf shelf_web_socket stack_trace stream_transform timing watcher web_socket_channel yaml] - json_serializable 6.7.1 [analyzer async build build_config collection json_annotation meta path pub_semver pubspec_parse source_gen source_helper] transitive dependencies: - _fe_analyzer_shared 67.0.0 [meta] - _flutterfire_internals 1.3.16 [collection firebase_core firebase_core_platform_interface flutter meta] - analyzer 6.4.1 [_fe_analyzer_shared collection convert crypto glob meta package_config path pub_semver source_span watcher yaml] - archive 3.4.9 [crypto path pointycastle] - args 2.4.2 - async 2.11.0 [collection meta] - boolean_selector 2.1.1 [source_span string_scanner] - build 2.4.1 [analyzer async convert crypto glob logging meta package_config path] - build_config 1.1.1 [checked_yaml json_annotation path pubspec_parse yaml] - build_daemon 4.0.1 [built_collection built_value crypto http_multi_server logging path pool shelf shelf_web_socket stream_transform watcher web_socket_channel] - build_resolvers 2.4.2 [analyzer async build collection convert crypto graphs logging package_config path pool pub_semver stream_transform yaml] - build_runner_core 7.2.11 [async build build_config build_resolvers collection convert crypto glob graphs json_annotation logging meta package_config path pool timing watcher yaml] - built_collection 5.1.1 - built_value 8.8.1 [built_collection collection fixnum meta] - characters 1.3.0 - checked_yaml 2.0.3 [json_annotation source_span yaml] - clock 1.1.1 - cloud_firestore_platform_interface 6.0.10 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - cloud_firestore_web 3.8.10 [_flutterfire_internals cloud_firestore_platform_interface collection firebase_core firebase_core_web flutter flutter_web_plugins js] - cloud_functions_platform_interface 5.5.28 [firebase_core flutter meta plugin_platform_interface] - cloud_functions_web 4.9.6 [cloud_functions_platform_interface firebase_core firebase_core_web flutter flutter_web_plugins] - code_builder 4.9.0 [built_collection built_value collection matcher meta] - collection 1.18.0 - convert 3.1.1 [typed_data] - crypto 3.0.3 [typed_data] - dart_style 2.3.4 [analyzer args collection path pub_semver source_span] - fake_async 1.3.1 [clock collection] - file 7.0.0 [meta path] - firebase_core_platform_interface 5.0.0 [collection flutter flutter_test meta plugin_platform_interface] - firebase_core_web 2.17.0 [firebase_core_platform_interface flutter flutter_web_plugins meta web] - firebase_storage_platform_interface 5.1.3 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface] - firebase_storage_web 3.6.17 [_flutterfire_internals async firebase_core firebase_core_web firebase_storage_platform_interface flutter flutter_web_plugins http js meta] - fixnum 1.1.0 - flutter_test 0.0.0 [flutter test_api matcher path fake_async clock stack_trace vector_math leak_tracker_flutter_testing async boolean_selector characters collection leak_tracker leak_tracker_testing material_color_utilities meta source_span stream_channel string_scanner term_glyph vm_service] - flutter_web_plugins 0.0.0 [flutter characters collection material_color_utilities meta vector_math] - frontend_server_client 3.2.0 [async path] - glob 2.1.2 [async collection file path string_scanner] - graphs 2.3.1 [collection] - http 1.2.1 [async http_parser meta web] - http_multi_server 3.2.1 [async] - http_parser 4.0.2 [collection source_span string_scanner typed_data] - io 1.0.4 [meta path string_scanner] - js 0.6.7 [meta] - leak_tracker 10.0.0 [clock collection meta path vm_service] - leak_tracker_flutter_testing 2.0.1 [flutter leak_tracker leak_tracker_testing matcher meta] - leak_tracker_testing 2.0.1 [leak_tracker matcher meta] - logging 1.2.0 - matcher 0.12.16+1 [async meta stack_trace term_glyph test_api] - material_color_utilities 0.8.0 [collection] - meta 1.11.0 - mime 1.0.4 - package_config 2.1.0 [path] - path 1.9.0 - petitparser 6.0.2 [meta] - plugin_platform_interface 2.1.7 [meta] - pointycastle 3.9.0 [collection convert js] - pool 1.5.1 [async stack_trace] - pub_semver 2.1.4 [collection meta] - pubspec_parse 1.2.3 [checked_yaml collection json_annotation pub_semver yaml] - shelf 1.4.1 [async collection http_parser path stack_trace stream_channel] - shelf_web_socket 1.0.4 [shelf stream_channel web_socket_channel] - sky_engine 0.0.99 - source_gen 1.5.0 [analyzer async build dart_style glob path source_span yaml] - source_helper 1.3.4 [analyzer collection source_gen] - source_span 1.10.0 [collection path term_glyph] - stack_trace 1.11.1 [path] - stream_channel 2.1.2 [async] - stream_transform 2.1.0 - string_scanner 1.2.0 [source_span] - term_glyph 1.2.1 - test_api 0.6.1 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph] - timing 1.0.1 [json_annotation] - typed_data 1.3.2 [collection] - vector_math 2.1.4 - vm_service 13.0.0 - watcher 1.1.0 [async path] - web 0.5.1 - web_socket_channel 2.4.0 [async crypto stream_channel] - xml 6.5.0 [collection meta petitparser] - yaml 3.1.2 [collection source_span string_scanner] ```

Additional context and comments

No response

russellwheatley commented 2 weeks ago

Hi @uhbsksingh - one way to help us help you would be to try debugging on the cloud functions emulator.

What to do:

  1. Ensure it throws exception on emulator
  2. Narrow down what is causing the bug by removing a couple of properties at a time (presumably you will have to match it on the emulator and redeploy).
  3. At some point, it will be successful and you will be able to narrow down what property is causing the exception. That way, we can figure out if the bug is a FlutterFire issue, firebase-ios-sdk or perhaps a misconfiguration.

Let me know how it goes 🙏

uhbsksingh commented 2 weeks ago

Hi @russellwheatley, the function works in the emulator. I sent the request using Postman to the emulator.

As for the properties, I sent the same JSON object I sent using Postman in callable.call. I get an exception like this

flutter: error.code: unknown
flutter: error.details: {}
flutter: error.message: An error occurred

The JSON object sent in the following code is the same one I sent on the body of the request sent using Postman to the emulator. The request runs successfully in the emulator but the same JSON object is not working when I send from my Flutter app to the deployed function. This is my call from Flutter to the deployed function.

final response = await callable.call({
        "data": {
          "activeCollectionId": "cYcoK45wFyBradLyQbhU",
          "action": {
            "action": "Add allocated purchase",
            "user": {
              "appUserId": "Hj9OD16vUuQVM1rDh30iIdcKCfk2",
              "fName": "Abhishek",
              "lName": "Singh",
              "imagePath": "mh_img_user/aa1692187748018"
            },
            "note": null
          },
          "purchaseBillList": [
            {
              "billID": "test-6102024-1",
              "reference": 1,
              "partner": {
                "partnerId": "test",
                "name": "test",
                "imagePath": null
              },
              "info": {
                "billDateTime":
                    "Timestamp(seconds=1718005730, nanoseconds=349154000)",
                "dueDateTime":
                    "Timestamp(seconds=1718005730, nanoseconds=349154000)",
                "dateTime":
                    "Timestamp(seconds=1718005730, nanoseconds=349154000)",
                "currency": "NPR",
                "action": {
                  "action": "Add allocated purchase",
                  "user": {
                    "appUserId": "Hj9OD16vUuQVM1rDh30iIdcKCfk2",
                    "fName": "Abhishek",
                    "lName": "Singh",
                    "imagePath": "mh_img_user/aa1692187748018"
                  },
                  "note": null
                },
                "paymentTerm": null,
                "attachments": null,
                "tags": null
              },
              "item": {
                "itemId": "d2mFoM7U0VEVESSEosIu",
                "name": "test",
                "unit": {
                  "unitId": "pieces",
                  "abbreviation": "pcs",
                  "allowDecimal": false
                },
                "imagePaths": null
              },
              "quantity": 1.0,
              "rate": 2200.0,
              "tax": {}
            }
          ],
          "transferEntityDocumentID": "9INPot7vBE4qwtFbEhkY",
          "appliedQuantity": 1.0,
          "billItemImageMap": {"d2mFoM7U0VEVESSEosIu": null}
        }
      });

Is there any other way I can decode the error?

russellwheatley commented 2 weeks ago

This is why I asked you to remove properties from the JSON object input (and removing use in emulated function) to see what is causing the exception. There might be an issue with properties being parsed. Might want to start with:

"billDateTime":
    "Timestamp(seconds=1718005730, nanoseconds=349154000)",
"dueDateTime":
    "Timestamp(seconds=1718005730, nanoseconds=349154000)",
"dateTime":
    "Timestamp(seconds=1718005730, nanoseconds=349154000)",

Keep on removing properties until it works, then you will know which properties are causing the exception as they will be the ones you last removed.

russellwheatley commented 2 weeks ago

I just noticed this in your cloud function:

      } catch (exception) {
        throw new HttpsError("unknown", "An error occurred", exception);
      }

why don't you wrap this around the entirety of the cloud function code within the function? This is exactly the exception you're getting in your flutter code by the way.

google-oss-bot commented 4 days ago

Hey @uhbsksingh. We need more information to resolve this issue but there hasn't been an update in 7 weekdays. I'm marking the issue as stale and if there are no new updates in the next 7 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!