1

I am trying to implement a location autocomplete feature in my Flutter App with the help of the Google Places API and its autocomplete feature. To keep the API-calling logic and the API Key away from the Client, the Flutter App should call a Firebase Cloud Function, which then calls the Google Places API. I have been trying to get this to work for several days now, worked through all kinds of articles and I just cannot make any progress. This is the source code of my Firebase Cloud Function:

import * as functions from "firebase-functions";
import * as axios from "axios";

export const autofillSuggestions = functions.region("europe-west3")
    .https.onCall(async (data) => {
      const input = data.input;
      const sessionToken = data.sessiontoken;

      // will be stored as an environment variable, as soon as i get cloud function to work
      const googleAPIKey = "my_api_key"; 
      

      const requestURL = "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=" + input +
     "&key=" + googleAPIKey + "&sessiontoken=" + sessionToken;

      return await axios.default.get(requestURL).then((apiResponse) => {
        return {predictions: apiResponse.data.predictions,
        };
      })
          .catch((error) => {
            console.log(error);
          });
    });

This source code gets imported into a index.ts which then exports the file again, so the issue should not lie there. The Cloud Function Calling Logic inside the Flutter App looks like this:

HttpsCallable _getCallable(String functionName) =>
      FirebaseFunctions.instance.httpsCallable(functionName);

 Future<dynamic> _getSuggestionsFromGoogleMapsAPI(String input) async {
    //FirebaseFunctions.instance
    //    .useFunctionsEmulator(origin: 'http://localhost:5001');
    final callable = _getCallable('autofillSuggestions');

    try {

      final data = HashMap.of({
        'sugg': input,
        'sessionToken': _sessionToken,
      });
      final response = await callable.call(data);
      return response.data;
    } catch (e) {
      print(e);
      throw AutocompletionFailure();
    }
  }

When I trigger the Cloud Function Call within the Flutter app and I uncomment the lines mentioning useFunctionsEmulator, everything works just fine. But as soon as I comment those out again and want to communicate with the real Firebase Server, I always get this error message in the VS Code debug console:

[firebase_functions/internal] Response is not valid JSON object.

The error log on the Firebase Server looks like this:

autofillSuggestions
{
 "@type":"type.googleapis.com/google.cloud.audit.AuditLog",
 "authenticationInfo":{"principalEmail":"my@mail.com"},
 "requestMetadata": 
   {
     "callerIp":"10.10.101.101",
     "callerSuppliedUserAgent":"FirebaseCLI/9.8.0,gzip(gfe),gzip(gfe)",
     "requestAttributes":{"time":"2021-04-04T09:34:15.120516Z","auth":{}},
     "destinationAttributes":{}
   },
 "serviceName":"cloudfunctions.googleapis.com",
 "methodName":"google.cloud.functions.v1.CloudFunctionsService.SetIamPolicy",
 "authorizationInfo":
   [
    {
     "resource":"projects/my-project/locations/europe-west3/functions/autofillSuggestions",
     "permission":"cloudfunctions.functions.setIamPolicy",
     "granted":true,
     "resourceAttributes":{}
    },
    {
     "permission":"cloudfunctions.functions.setIamPolicy",
     "granted":true,
     "resourceAttributes":{}
    }
   ],
 "resourceName":"projects/my-project/locations/europe-west3/functions/autofillSuggestions",
 "request":
   {
    "updateMask":"version,bindings",
    "resource":"projects/my-project/locations/europe-west3/functions/autofillSuggestions",
    "policy":
      {
       "bindings":
         [
          {
           "members":["allUsers"],
           "role":"roles/cloudfunctions.invoker"
          }
         ]
      },
    "@type":"type.googleapis.com/google.iam.v1.SetIamPolicyRequest"
   },
 "response":
   {
    "bindings":
        [
         {
          "members":["allUsers"],
          "role":"roles/cloudfunctions.invoker"
         }
        ],
    "@type":"type.googleapis.com/google.iam.v1.Policy","etag":"BwW/IkjRmog="
   },
 "resourceLocation":{"currentLocations":["europe-west3"]}
}

Since it does not mention anything like Function execution started or Function execution terminated with ... I suspect that the Cloud Function does not get called at all... Am I right with this thought? If yes, does anyone know how to get this working???

I oriented myself heavily on this and this article. I also found this pretty interesting, haven't tried it out yet, though, because I thought my main issue is the Cloud Function not even being called...

Nimantha
  • 6,405
  • 6
  • 28
  • 69
tschulayen
  • 61
  • 7

1 Answers1

4

The answer is - of course - quite stupid, but I guess it is the curse of IT and auto-didacts... As you can see, I specified europe-west3 as the region of my Cloud Function, due to my location. But when getting the callable reference, I did not specify a region, which led the cloud_functions flutter package to create a FirebaseFunctions instance with the default region set to us-central1. This messed up the call and led to the internal error. The easy fix is within the flutter app:

New implementation of _getCallable :

HttpsCallable _getCallable(String functionName) {
   return FirebaseFunctions.instanceFor(region: 'europe-west3')
       .httpsCallable(functionName);
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
tschulayen
  • 61
  • 7