I recently started developing an app using Flutter and Firebase. I use Firebase Emulator to test Authentication and Cloud Functions. Most of my code is in the Firebase Cloud Functions which I use for all CRUD for Firestore and RTDB. While adding some new features, I got this error in my app. I tried searching a lot but could not find any solution. The following is the error is receive:
An error occured while calling function profile-get
Error Details: null
Message: An internal error has occurred, print and inspect the error details for more information.
Plugin: firebase_functions
Stacktrace: null
My API class in Flutter:
class Api {
Api(this.functions);
final FirebaseFunctions functions;
static Api init() {
FirebaseFunctions functions = FirebaseFunctions.instance;
if (emulator) functions.useFunctionsEmulator(origin: host);
return Api(functions);
}
Future<ApiResult> call(String name, {
Map<String, dynamic> parameters,
}) async {
try {
HttpsCallable callable = functions.httpsCallable(name);
HttpsCallableResult results = await callable.call(parameters);
return ApiResult(new Map<String, dynamic>.from(results.data));
} on FirebaseFunctionsException catch (e) {
print('An error occurred while calling function $name.');
print('Error Details: ${e.details}\nMessage: ${e.message}\nPlugin: ${e.plugin}\nStacktrace: ${e.stackTrace}');
return ApiResult({
'status': 'error',
'message': 'An error occured',
'code': 'unknown'
});
}
}
static String get host => Platform.isAndroid ? 'http://10.0.2.2:2021' : 'http://localhost:2021';
}
I tried running the functions directly from their local URL and they work fine.
As mentioned in the comments defore you are reating a cloud function with onRequest. Those are not callable using an SDK but only trough https URL.
To create a callable function that you can call trough Firebase SDKs you would need to refactor your functions to use the onCall.
It should look something like this:
exports.yourFunctionName= functions.https.onCall((data, context) => {
// receive the data
const text = data.text;
// return a response
return {
test:'test'
}
});
Here you have more information how the callable functions work.
Are you using a different region than the standard us-central1? This is often the case, so you need to change the region you are calling from
HttpsCallable callable = FirebaseFunctions.instanceFor(region:"your_region").httpsCallable(name);
Related
I am very new with the MergeMap method to combine multiple API requests. I have following and reading this tutorial how to do it.
https://levelup.gitconnected.com/handle-multiple-api-requests-in-angular-using-mergemap-and-forkjoin-to-avoid-nested-subscriptions-a20fb5040d0c
This is my code to get data from an API:
Service.ts
getPriceList(): Observable<Price[]> {
this.http.get<Price[]>(this.baseUrl).pipe(
map( priceId => {
const id = priceId[0];
this.priceId = id.id;
return id;
}),
mergeMap( Id=> this.http.get(this.baseUrl2 + /${Id.id})),
).subscribe( productPrices => {
this.productPrices = productPrices;
});
}
I need all the data from the first baseUrl, but I assign the Id to use it in my second api request. I call this method in home.ts like this:
this.dataService.getPriceList().subscribe(response => {
console.log(response);
this.priceListData = response;
There is no error when I call this method in the file.
The error is in the method from Service.ts
A function whose declared type is neither 'void' nor 'any' must return
a value.ts(2355)
I am using the return statement in the first api request.
I have tried this solution:
A function whose declared type is neither 'void' nor 'any' must return a value
Then I get the following error:
Type 'Subscription' is missing the following properties from type
'Observable<Crypto[]>': _isScalar, source, operator, lift, and 6
more.ts(2740)
How to use the MergeMap the right way in Angular 11 / ionic 6?
I think you should return something on getPriceList, which you weren't, and if you subscribe inside getPriceList there is no observable anymore to subscribe after that
getPriceList(): Observable<Price[]> {
return this.http
.get<Price[]>(this.baseUrl)
.pipe(
map(priceId => {
const id = priceId[0];
this.priceId = id.id;
return id;
}),
mergeMap(id => this.http.get(`this.baseUrl2${id.id}`))
)
}
and the call
this.dataService.getPriceList().subscribe(response => {
console.log(response);
this.priceListData = response;
});
I'm in need of your expertise in React Native.
I'm trying to use expo-local-authentication for local fingerprint authentication for my application.
My project was created using expo init command.
I have done the setup as per the documentation and still running into a strange issue:
Below is the error I'm facing for LocalAuthentication.authenticateAsync(options):
Native method ExpoLocalAuthentication.authenticateAsync expects 0
arguments but received 1
Here is the required part of my code:
import * as LocalAuthentication from 'expo-local-authentication';
const authenticate = async () => {
const hasHardwareAsync = await LocalAuthentication.hasHardwareAsync();
if (hasHardwareAsync) {
const supportedAuthentications = await LocalAuthentication.supportedAuthenticationTypesAsync();
if (supportedAuthentications.indexOf(1) !== -1) {
// Finger print supported
const isFingerprintEnrolled = await LocalAuthentication.isEnrolledAsync();
if (isFingerprintEnrolled) {
const options = {
promptMessage: 'Authenticate yourself',
};
try {
// Also tried with await but it throws the same error
// await LocalAuthentication.authenticateAsync(options)
LocalAuthentication.authenticateAsync(options).then(result => {
// I never get inside this block
console.warn(result)
})
.catch(error => {
console.warn('Authentication Error: ', error)
})
} catch (error) {
console.warn(error)
}
}
}
}
}
Not sure what I'm missing. Seems like there is no information available about the error. I also tried to run the LocalAuthentication.authenticateAsync() without any arguments but it still throws the same error.
Any help on what could be the root cause of the issue and how can I resolve it or any other alternative for local authentication would be highly appreciated.
Update your app to the latest version of expo (38 in my case) and to the latest version of expo-local-authentication, and the error goes away.
I'm currently trying to send data from an app to Firebase Cloud Functions.
This is my code in Android:
val hashMap = hashMapOf<String, Any>()
hashMap.put("uid", uid)
val result = FirebaseFunctions.getInstance()
.getHttpsCallable("getUserDetails")
.call(hashMap).await()
And this is my TypeScript code:
export const getUserDetails = functions.https.onRequest(async (request, response) => {
try {
console.log("uid" + request.rawBody + request.body.toString() + request.query.data
+ request.params.data)
..........................
}
I tried request.param("","") is deprecated.
I tried with request.params.uid and I tried parsing with JSON but nothing is working.
In the log I can see that request.rawBody has the data but I don't know how to get to the specific field I want
You are mixing up Callable Cloud Functions and HTTP Cloud Functions.
You Cloud Function code corresponds to an HTTP one (functions.https.onRequest(...)) but the code in your front-end calls a Callable one (FirebaseFunctions.getInstance().getHttpsCallable("getUserDetails")).
You should adapt one or the other, most probably adapt your Cloud Function to a Callable one, along the following lines:
export const getUserDetails = functions.https.onCall(async (data, context) => {
try {
const uid = data.uid;
// ..........................
}
I am trying to invoke a Firebase https callable function and I get an error in Android Studio saying "com.google.firebase.functions.FirebaseFunctionsException: Response is not valid JSON object"
Here is my code
index.ts file
import * as functions from 'firebase-functions'
const admin = require('firebase-admin')
admin.initializeApp()
export { newUserSignUp } from './userCreated'
export { userDeleted } from './userDeleted'
//this is the function that the client is unable to call
exports.sendFeedback = functions.region('asia-east2').https.onCall((data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'unauthenticated',
'only authenticated users can add requests'
)
}
if (data.text.length > 30) {
throw new functions.https.HttpsError(
'invalid-argument',
'request must be no more than 30 characters long'
)
}
return admin.firestore().collection('Feedback').add({
Feedback : data.text,
uid: context.auth.uid
})
})
Here is the code in my .kt Activity file in Android Studio
private fun sendFeedbackViaCloudFunction() {
// Create the arguments to the callable function.
val data = hashMapOf(
"text" to write_feedback_edit_text.toString(),
"uid" to FirebaseAuth.getInstance().currentUser!!.uid
)
functions = FirebaseFunctions.getInstance()
Timber.i("Calling the cloud function")
functions
.getHttpsCallable("sendFeedback")
.call(data)
.addOnFailureListener {
//This is the line thats printing the error log statement
Timber.i("Failed to invoke the sendFeedback function: $it")
}
.continueWith { task ->
val result = task.result?.data as String
result
}
}
Error statement threw by Android Studio: com.google.firebase.functions.FirebaseFunctionsException: Response is not valid JSON object.
Ok I resolved this, So basically I was getting the "response is not valid JSON object error" repeatedly even after I set a promise to return a valid JSON in my index.ts file.
So apparently if a client is calling a function then the client also needs to specify the server region if the default us-central1 is not the preferred server location. So I had specified the 'asia-east2' as my preference on the server-side but not at the client-side.
Soon as I added the below line at the client side, works perfectly now
functions = FirebaseFunctions.getInstance("asia-east2")
I would like to write data in Firebase and I get a permission error, here is what I tried :
void initState() {
super.initState();
testFirebase();
}
Future testFirebase() async {
initUser();
//Initialize Firebase
final FirebaseApp firebaseApp = await FirebaseApp.configure( ... );
final FirebaseDatabase database = new FirebaseDatabase(app: firebaseApp);
database.reference().child('counter').push().set(<String, String>{
'var': 'test'
});
}
Future initUser() async {
googleUser = await _ensureLoggedInOnStartUp();
if (googleUser == null) {
setState(() {
state.isLoading = false;
});
} else {
var firebaseUser = await logIntoFirebase();
}
}
Here is my Firebase rules :
The google-services.json file is added to the app root directory :
Result :
I get the following error message :
I tried also with :
push().setValue('2')
but it doesn't work and that makes me crazy, I don't understand...
Any idea?
Quick initial check is that you need to await initUser(). So:
Future testFirebase() async {
await initUser();
//Initialize Firebase
final FirebaseApp firebaseApp = await FirebaseApp.configure( ... );
Without that I'd expect the calls to the database to start before auth has finished.
Update: I just verified that is indeed the case with this simple code:
void _signin() async {
print("Calling __actuallySignin");
__actuallySignin();
print("called __actuallySignin and waited");
}
void __actuallySignin() async {
print("Calling signIn...");
await FirebaseAuth.instance.signInAnonymously();
print("called signIn... and waited");
}
This prints:
flutter: Calling __actuallySignin
flutter: called __actuallySignin and waited
flutter: Calling signIn...
...
flutter: called signIn... and waited
So the __actuallySignin method is done before the sign in is done. To make the calling code wait for the result you add await:
await __actuallySignin();
Which outputs:
flutter: Calling __actuallySignin
flutter: Calling signIn...
flutter: called signIn... and waited
flutter: called __actuallySignin and waited
Just adding await to initUser() didn't work, the database was also not correctly adressed.
Solution :
FirebaseDatabase.instance.reference().child ...
instead of :
final FirebaseApp firebaseApp = await FirebaseApp.configure( ... );
final FirebaseDatabase database = new FirebaseDatabase(app: firebaseApp);
I was able to fix this problem by not accessing the database reference using the method listed in the plugin documentation:
final FirebaseDatabase database = FirebaseDatabase(app: app);
this.databaseRef = database.reference();
But instead making use of this approach:
this.databaseRef = FirebaseDatabase.instance.reference();
When you work with multiple pieces of Flutter documentation, you will see that many approaches work for reading public information from the Real Time Database, but only one method worked for reading information that depends on the auth variable. This was my experience.
Working method: https://marcinszalek.pl/flutter/firebase-database-flutter-weighttracker/
Non-working method was associated with the plugin documentation: https://pub.dartlang.org/packages/firebase_database#-example-tab-
Also, my project was configured using the main Firebase Flutter documentation for linking my app to Firebase: https://firebase.google.com/docs/flutter/setup