Unable to use a Callable Cloud Function in Flutter - firebase

I'm currently using a Firebase Cloud Function, which I wish I can use on Call to check if an Image has adult content, and see some of the tutorials out there I manage to create this function:
exports.checkImage = functions.https.onCall(async (data, context) => {
const img_base64 = data;
const request = {
image: {
content: img_base64
}
};
try {
const [result] = await client2.safeSearchDetection(request);
console.log(result.safeSearchAnnotation?.adult);
return result.safeSearchAnnotation?.adult;
} catch (error) {
console.log(error);
return;
}
return;
});
Now I'm tried to send the Image using an iOS Emulator with that call Function in 2 ways:
First Option I tried:
1.
exports.checkImage = functions.https.onCall(async (data, context) => {
const img_base64 = data;
const request = {
image: {
content: img_base64
}
};
try {
const [result] = await client2.safeSearchDetection(request);
console.log(result.safeSearchAnnotation?.adult);
return result.safeSearchAnnotation?.adult;
} catch (error) {
console.log(error);
return;
}
return;
});
The problem here is I get this Error on my Debugging Console:
[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: MissingPluginException(No implementation found for method FirebaseFunctions#call on channel plugins.flutter.io/firebase_functions)
#0 convertPlatformException (package:cloud_functions_platform_interface/src/method_channel/utils/exception.dart:16:5)
#1 MethodChannelHttpsCallable.call (package:cloud_functions_platform_interface/src/method_channel/method_channel_https_callable.dart:39:13)
<asynchronous suspension>
#2 HttpsCallable.call (package:cloud_functions/src/https_callable.dart:35:37)
<asynchronous suspension>
#3 DatabaseMethods.clearImage
package:property1/providers/database.dart:294
In someplace I read that the onCall Cloud Functions can be use as regular End Points so I did this:
clearImage(imageX) async {
final url = Uri.parse(
'https://us-central1-XXXXXXX-XXXX.cloudfunctions.net/checkImage',
);
try {
final response = await http.post(
url,
body: json.encode({
"image": {
"content": imageX
}),
);
}
}
In this one, I only get an Invalid Request unable to process.
I even try this from Postman getting the same result bad request.
As additional data I call this function from where I select the Image with this code:
void _pickImage() async {
final imagePicker = ImagePicker();
final pickedImageFile = await imagePicker.getImage(
source: ImageSource.gallery,
imageQuality: 50,
);
setState(() {
if (pickedImageFile != null) {
_pickedImage = File(pickedImageFile.path);
}
});
// This section will be used to check the Images with Google Vision.
List<int> imageBytes = _pickedImage.readAsBytesSync();
String base64Image = base64Encode(imageBytes);
DatabaseMethods().clearImage(base64Image);
widget.imagePickFn(File(pickedImageFile.path));
}
Basically, I convert the image to a Base64 encoded image that I send so Cloud Vision could work with it.
I even try to just check if I get to the Cloud Function by Commenting out the try of Cloudvision and just adding a console log to get what data is receiving but it is never reached, always an Invalid request, unable to process is what I get.
Any ideas on why I'm not reaching the Function, or on how I can test this from my iOS Emulator.
Kind Regards,

For calling onCall cloud functions from flutter apps, you have to use FirebaseFunctions.instance.httpsCallable():
import 'package:cloud_functions/cloud_functions.dart';
try {
final result =
await FirebaseFunctions.instance.httpsCallable('checkImage').call();
} on FirebaseFunctionsException catch (error) {
print(error.code);
print(error.details);
print(error.message);
}
First, install the package:
flutter pub add cloud_functions
Here's the documentation.

Related

How to handle next api route using dynamic id

I am using NextJS and created three layers that separate the logic.
The purpose is to minimise the error handling to the getServerSideProps section. I want to get all the lists from the database.
In the first layer, in the API route I created a folder called get-all-lists and a file [userId].js. The get request will be 'http://localhost:3000/api/get-all-lists/iudga937gr8'. Bellow there is the api route that get all the lists with the help of Prsima. It is working perfectly
import prisma from '../../../lib/prisma'
export default async function handler(req, res) {
const { userId } = req.query;
if (req.method === 'GET') {
try {
const shoppingLists = await prisma.List.findMany({ where: { userId: userId }});
res.status(200).json({lists: shoppingLists});
}
catch (error) {
console.log(error);
res.status(500).json({ message: 'Something went wrong. Please try again'});
}
}
else {
res.status(500).json({message: 'Invalid method requested!'});
}
}
The next layer, is the abstraction one which sent the final result to getServerSideProps. I created this because I need to fetch alot of requests and it would be too messy...
export const getAllLists = async userId => {
try {
const lists = await axios.get(`/api/get-all-lists/${userId}`);
return lists;
}
catch (error) {
console.log('Abstraction layer error: ', error);
return 'Something went wrong. Please try again later';
}
}
The problem arise here. In the postman I have the right result. In postman I use http://localhost:3000/api/get-all-lists/clbcpc0hi0002sb1wsiea3q5d and the server sent me the array specified.
But this function does not work and send me this error:
Abstraction layer error: TypeError [ERR_INVALID_URL]: Invalid URL
at new NodeError (node:internal/errors:371:5)
at onParseError (node:internal/url:552:9)
at new URL (node:internal/url:628:5)
at dispatchHttpRequest (file:///Users/sasdaniel/Desktop/massage/node_modules/axios/lib/adapters/http.js:176:20)
at new Promise (<anonymous>)
at http (file:///Users/sasdaniel/Desktop/massage/node_modules/axios/lib/adapters/http.js:112:10)
at Axios.dispatchRequest (file:///Users/sasdaniel/Desktop/massage/node_modules/axios/lib/core/dispatchRequest.js:51:10)
at Axios.request (file:///Users/sasdaniel/Desktop/massage/node_modules/axios/lib/core/Axios.js:142:33)
at Axios.<computed> [as get] (file:///Users/sasdaniel/Desktop/massage/node_modules/axios/lib/core/Axios.js:168:17)
at Function.wrap [as get] (file:///Users/sasdaniel/Desktop/massage/node_modules/axios/lib/helpers/bind.js:5:15) {
input: '/api/get-all-lists/clbcpc0hi0002sb1wsiea3q5d',
code: 'ERR_INVALID_URL'
}
I also tried to paste the localhost in the browser and it have no problem.
You could extract the functionality into /lib/getAllList.js:
import prisma from './prisma';
export default async function getAllLists(userId) {
return await prisma.List.findMany({ where: { userId: userId }});
}
Then use it in your API route:
import getAllLists from '../../../lib/getAllLists';
export default async function handler(req, res) {
const { userId } = req.query;
if (req.method === 'GET') {
try {
const shoppingLists = await getAllLists(userId);
res.status(200).json({lists: shoppingLists});
}
catch (error) {
console.log(error);
res.status(500).json({ message: 'Something went wrong. Please try again'});
}
}
else {
res.status(500).json({message: 'Invalid method requested!'});
}
}
Then use it in getServerSideProps:
import getAllLists from 'path/to/lib/getAllLists';
export async function getServerSideProps(context) {
const { userId } = context.params;
const shoppingLists = await getAllLists(userId);
return {
props: {
shoppingLists,
},
};
}

Google Functions - httpCallable.onRequest display error message [duplicate]

I am trying to update some values that I will enter from my Flutter app to FireStore using Cloud Functions. Here is my code so far:
This is my Cloud function (in JavaScript, index.js) to update a document in FireStore:
exports.update = functions.https.onRequest((req, res) => {
const getNewPercentage = req.body;
const getDocument = admin.firestore().collection('waterpercentage').doc('percentage');
getDocument.get().then((doc) => {
if(doc.exists) {
getDocument.update({'percentage': getNewPercentage}).catch(err => {
console.log("Error: ", err);
res.send("500");
})
}
}).catch(err => {
console.log("Error: ", err)
res.send("500");
});
res.send(200);
})
In Flutter, here's what I tried:
Future<void> updateWaterPercentage() async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('update');
//final results = await callable.call(<dynamic, int>{'percentage' : percentage.round()});
log("Calling percentage here: ");
log(percentage.round().toString());
dynamic resp = await callable.call(<double, dynamic> {
percentage : percentage.round(),
});
When I call updateWaterPercentage() from a Button in Flutter, the data doesn't get updated in FireStore. I also tried using:
CloudFunctions.instance.call(
functionName: "update",
parameters: {
"percentage": percentage.round(),
}
);
However, even though I imported 'package:cloud_functions/cloud_functions.dart'; on top, Flutter doesn't recognize CloudFunctions. How can I get the code to call update that takes in a parameter to correctly update a value in Firestore?
You are mixing Callable Cloud Functions and HTTPS Cloud Functions.
Your Cloud Function code corresponds to an HTTP one (functions.https.onRequest((req, res) => {(...)}) but the code in your app declares and calls a Callable one (HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('update');).
In addition, in your HTTPS Cloud Function code, you send back a response before the asynchronous operation is complete (see the last line res.send(200);).
So, to fix the problem:
You could call the HTTPS Cloud Function from your flutter app with the http package;
But the best would probably be to adapt your Cloud Function to be a Callable one in order to get the advantages of a Callable, including the use of the cloud_functions package which makes very easy to call the CF from your app. Something along the following lines.
exports.update = functions.https.onCall((data, context) => {
const getNewPercentage = data.percentage;
const documentRef = admin.firestore().collection('waterpercentage').doc('percentage');
return documentRef.get()
.then((doc) => {
if (doc.exists) {
return documentRef.update({ 'percentage': getNewPercentage });
} else {
throw new Error('Doc does not exist');
}
})
.then(() => {
return { result: "doc updated" };
})
.catch(err => {
console.log("Error: ", err)
// See the doc: https://firebase.google.com/docs/functions/callable#handle_errors
});
});

Flutter Web Firebase storage error response storage/unkown

hope all is well.
I have been trying to upload an image or file to firebase storage from flutter web. Once I try to call put data the console just reads
Firebase Storage: An unknown error occurred, please check the error payload for server response. (storage/unknown)
I don't know how to check server response when using a plugin but this error comes from the try-catch block with on firebaseexception print error
Code: XFile? pickedImage;
_startFilePicker() async {
pickedImage = await ImagePicker().pickImage(
source: ImageSource.camera,
imageQuality: 60,
maxWidth: 250,
);
setState(() {
_hasUploaded = true;
});
uploadImageToStorage(pickedImage);
}
String uploadedPhotoUrl = '';
uploadImageToStorage(XFile? pickedFile) async {
try {
final String url = FirebaseStorage.instance.bucket;
Reference _reference = FirebaseStorage.instance
.refFromURL(
'gs://genderbasedviolence-bd860.appspot.com/') //${FirebaseAuth.instance.currentUser!.uid}
.child('images');
final bytes = await pickedFile!.readAsBytes();
await _reference.putData(bytes);
} on FirebaseException catch (e) {
print(e.code);
}
}
Please let me know if you need more info. Thanks

#firebase/firestore: Firestore (8.2.2): Connection webchannel transport errored: [object, Object] when trying to cache in React Native

I am trying to cache items that get pulled from a Firebase database, I'm using React Native.
Getting and posting data works fine, the problem happens when trying to cache the data.
When internet gets turned off it shows the error:
#firebase/firestore: Firestore (8.2.2): Connection webchannel transport errored: [object, Object]'
when trying to cache.
For caching I use the "#react-native-async-storage/async-storage": "~1.15.0", library.
In the code below I get data and store it. When the internet is turned off the product stays visible for a few seconds then it throws the error and disappears.
The intended behaviour for this code is that the item stays on screen even when internet is off.
How do I fix this problem? Or is there a better way to sort out caching?
async function getSelfMadeItems() {
let list = [];
if (getIsConnected === true) {
let snapshot = await firestore
.collection("SelfMadeProducts")
.where("UserUID", "==", user.uid)
.orderBy("MadeOn", "asc")
.limit(5)
.get()
.then(async (querySnapshot) => {
querySnapshot.forEach((doc) => {
list.push({
MadeOn: doc.data().MadeOn.toDate().toDateString(),
//ImageUri: doc.data().ImageUri,
id: doc.id,
});
});
try {
const jsonValue = JSON.stringify(list);
if (!querySnapshot.empty) {
await AsyncStorage.setItem("#SelfMade", jsonValue);
}
} catch (e) {
console.log(e);
}
setproductsState(list);
});
} else {
try {
const ItemValue = await AsyncStorage.getItem("#SelfMade");
if (ItemValue !== null) {
const jsonValue = await AsyncStorage.getItem("#SelfMade");
console.log("Json", JSON.parse(jsonValue));
}
jsonValue != null ? setproductsState(JSON.parse(jsonValue)) : null;
} catch (e) {
console.log(e);
}
}```

how to download a file in dart using Stream and Completer

I need to download a huge file in dart and also show progress while downloading it.
So I implemented this function:
import 'dart:io';
Future<void> download(String url, String path) async {
HttpClient client = HttpClient();
final request = await client.getUrl(Uri.parse(url));
request.close().then((HttpClientResponse response) async {
final file = File(path).openWrite(mode: FileMode.writeOnlyAppend);
await response.listen((data) {
file.add(data);
// show progress bar
...
}, onError: (e) {
throw e;
}, onDone: () {
file.close().then((e) {
print("Done");
return;
});
});
});
}
This works great with for downloading 1 file at a time.
But I want to able to call it in a loop and control the number of downloads that can occur at once.
Which brings me to this question on how to implement this using a Stream and Completer.

Resources