Flutter Firebase Cloud function can not be called - flutter

I am getting an error whilst using Firebase Cloud Functions when I try to call a callable function from Flutter.
flutter: caught generic exception
flutter: PlatformException(functionsError, Firebase function failed with exception., {message: NOT FOUND, code: NOT_FOUND})
Here is how I try to call the cloud function with using cloud_functions: ^0.4.2+3
import 'package:cloud_functions/cloud_functions.dart';
_check(String id) async {
HttpsCallable callable = CloudFunctions.instance
.getHttpsCallable(functionName: 'checkUserFavorites');
try {
final HttpsCallableResult result = await callable.call(
<String, dynamic>{
'id': id,
},
);
print(result.data);
} on CloudFunctionsException catch (e) {
print('caught firebase functions exception');
print(e.code);
print(e.message);
print(e.details);
} catch (e) {
print('caught generic exception');
print(e);
}
}

I have experienced similar issues, and with few days of debugging and experimenting I found the solution only after studying the source code of Cloud Functions Plugin for Flutter.
When you deploy Firebase Cloud function, you can choose any region of preference (closer to your application the better). For example
// using DigitalOcean spaces
exports.generateCloudImageUrl = functions
.region('europe-west3')
.https.onCall((reqData, context) => {
...
}
When you want to call this function from Flutter app, you must specify the region, otherwise all goes to us-central1 which is default. See example code on how to use a function deployed in a specific region
final HttpsCallable generateCloudImageUrl = new CloudFunctions(region: "europe-west3")
.getHttpsCallable(functionName: 'generateCloudImageUrl');
// NB! if you initialize with 'CloudFunctions.instance' then this uses 'us-central1' as default region!
see cloud_function source for init.
Update, as of recent release, you can initialize as below;
FirebaseFunctions.instanceFor(region: "europe-west3").httpsCallable(
"generateCloudImageUrl",
options:
HttpsCallableOptions(timeout: const Duration(seconds: 30)));

Cloud functions are supported in the regions that you are currently running them, according to the Cloud Functions Location Documentation, but not in all regions.
According to what you shared in the comments, I would say that there are 3 cenarios to your issue:
europe-west1: The function is probably out of date, since you are getting an unespected data format error, which suggest that it expects different data/format than your default function.
europe-west2: The function is not deployed in this region, this is hinted in the error message message: NOT FOUND.
Default Function (unknown region): This is the most recent version of the function, on a region different than europe-west1 and europe-west2, and it accepts the call
with the data in the format that you are sending.
NOTE: You can check which regions you currently have your cloud function deployed on the cloud functions dashboard, as you can see on the example image below:
Also, I suspect that the default region you using is us-central1, since according to the documentation:
By default, functions run in the us-central1 region
To fix your issue, I suggest that you redeploy your current version of the function to the europe-west regions that you intend to use.

There are three reasons this error mostly happens:
1. Call the correct function:
Make sure to call the correct function in its full name (visible when you start a local emulator). Espacially if you have additional exports of files in your index.js file make sure to call the export name as well.
Syntax: serverLocation-optionalExportParent-yourFunction
Example: us-central1-post_functions-updateShare
Note that the server location can also be configured in your instance
2. Emulator: Same WIFI
Make sure to be connected to the same wifi, when using the emulator. Otherwise, any call will end in unvailablity resulting in
Unhandled Exception: [firebase_functions/unavailable] UNAVAILABLE
3. Emulator: Correct host configuration
To connect to a physical device the host at all emulators at your firebase.json must be configured: Simply add "host": "0.0.0.0".
Now the host in flutter needs to be your ip-adress of the computer. More on that here

In my case, in addition to the regional issue, what really solved to me was to include the script below in index.html:
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-functions.js"></script>

Related

Adding NFT to certified collection using the metaplex.nfts().create() function

I am having issues verifying the collection when I mint an NFT. I am using the metaplex.nfts().create() function and I am positive the metaplex.indentity() has update authority of the collection NFT.
I get the following error.
MetaplexError: TokenMetadataProgram > Cannont Verify Collection in this Instruction
>> Source: Program > TokenMetadataProgram [metaqbxx...x1s]
>> Problem: The program [TokenMetadataProgram] at address [meta...x1s] raised an error of code [74] that translates to "Cannont Verify Collection in this Instruction".
>> Solution: Check the error message provided by the program.
Caused By: CollectionCannotBeVerifiedInThisInstruction: Cannont Verify Collection in this Instruction
I also know that everything else involved with the minting of the NFT is fine because when I add the collection with verified: 0 it works fine.
Am I able to add an NFT to an existing certified collection using the metaplex.nfts().create() function? If that is not allowed, what is the best way to do so immediately after minting?
Full stack trace:
at RpcClient.parseProgramError (file:///.../node_modules/#metaplex-foundation/js/dist/esm/plugins/rpcModule/RpcClient.mjs:206:28)
at RpcClient.sendTransaction (file:///.../node_modules/#metaplex-foundation/js/dist/esm/plugins/rpcModule/RpcClient.mjs:48:18)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async RpcClient.sendAndConfirmTransaction (file:///.../node_modules/#metaplex-foundation/js/dist/esm/plugins/rpcModule/RpcClient.mjs:69:23)
at async TransactionBuilder.sendAndConfirm (file:///.../node_modules/#metaplex-foundation/js/dist/esm/utils/TransactionBuilder.mjs:131:22)
at async file:///.../node_modules/#metaplex-foundation/js/dist/esm/utils/Task.mjs:58:23
at async Disposable.run (file:///.../node_modules/#metaplex-foundation/js/dist/esm/utils/Disposable.mjs:22:14)
at async Task.callback (file:///.../node_modules/#metaplex-foundation/js/dist/esm/plugins/nftModule/NftClient.mjs:48:22)
at async file:///.../node_modules/#metaplex-foundation/js/dist/esm/utils/Task.mjs:58:23
at async Disposable.run (file:///.../node_modules/#metaplex-foundation/js/dist/esm/utils/Disposable.mjs:22:14)
at async mintNft (file:///.../src/mint/mint_lib.js:46:29)
at async mint (file:///.../src/mint/mint_lib.js:71:12)
at async Context.<anonymous> (file:///.../test/mint-test.js:27:20)
The problem is that you try to set the collection as verified true without using a verify_collection instruction. This is not possible since the mpl_token_metadata program checks if someone tries to verify a collection without using that type of instruction, check the onchain code here.
So the actual way of doing this is create the NFT with the collection not verified (verified: 0) and then use createSetAndVerifyCollectionInstruction providing the respective accounts.

Add support for returning stream from an isolate

I have started using isolates and returned a value from my isolate as if it was a function.
Then I wanted to change the code to return stream, instead of future.
From this
compute<List<String>, List<String>>(
(params) async { ... return ...;
I wanted to change to this
compute<List<String>, Stream<String>>(
(params) async* { ... yield ...;
But I get the error:
The argument type 'Stream<FutureOr<Stream>>
Function(List)' can't be assigned to the parameter type
'FutureOr<Stream> Function(List)'. (Documentation)
If I understand correctly we can transfer data back "as stream" by opening ports and sending data through it.
Wouldn't it be better if the syntax will stay the same as "Stream function" and the implementation will do that for us?.
When opening ports you need to handle some extra staff like checking if the port is free and changing to a different port number if it doesn't.
Some extra explanation why opening a port is not needed when returning a single value and is needed when returning a stream will be welcome as well.
Should we ask for Dart team to make the change for isolates to support stream as well?.
Related to dart vm send back stream from isolate
I have found isolate_contactor package that can do that.
You can send results from inside the package isolate with
channel.sendResult()
Like a using a yield.

How to make "compute()" function insert data to sqlite while in isolated process?

I'm working on flutter app that uses php apis for server and sqlite for local data.
The problem is with "compute()".
Here is the explanation :
I have three functions that receives data from api on the server, then add the data to my local database (sqlite) table.
First function to get data from server.
Future<List<Map<String, dynamic>>> getServerData(int vers)async {
//my code
}
Second function to insert data into local database:
Future<int> addNewData(List<Map<String, dynamic>>)async {
//my code
}
Third function to call the first and second function:
Future<bool> checkServerData(int vers)async {
List<Map<String, dynamic>> sdt= await getServerData(vers);
int res=await addNewData(sdt);
if(res>0) return true;
else return false;
}
I want to call the third function in a compute function:
compute(checkServerData, 2);
When did that I found this error:
null check operator used on null value.
Note*:
If I used it without calling local database it works good.
The error appears if I called the database to insert data into.
When I searched about this issue I found that it's not allowed to access any resources which generated in one thread from another thread. But I didn't understand exactly how to resolve it or how to use another way that do the same idea.
After searching about the issue specified, I found those workaround solutions:
1: if the process is very important to work in background, you can use the Isolate package classes and functions which allow all isolated processes or contexts to share data between them as messages sending and receiving. But it's something complex for beginners in flutter and dart to understand these things, except those who know about threading in another environments.
To now more about that I will list here some links:
Those for flutter and pub documentation:
https://api.flutter.dev/flutter/dart-isolate/dart-isolate-library.html
https://api.flutter.dev/flutter/dart-isolate/Isolate-class.html
https://pub.dev/packages/flutter_isolate
This is an example in medium.com website:
https://medium.com/flutter-community/thread-and-isolate-with-flutter-30b9631137f3
2: the second solution if the process isn't important to work on background:
using the traditional approaches such as Future.builder or async/await.
You can know more about them here:
https://www.woolha.com/tutorials/flutter-using-futurebuilder-widget-examples
https://dart.dev/codelabs/async-await
and you can review this question and answers in When should I use a FutureBuilder?

How to use google cloud functions in flutter?

Now this is the index.js file
import 'dart:math';
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Cloud Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
exports.getRandomNumbers=functions.https.onCall((num){
const numbers=admin.firestore().collection('RandomNumbers').document('CurrentRandomNumber');
return numbers.add({
RandomNumber=new Random().nextInt(10);
});
});
and the function to do the task
onPressed: (){
CloudFunctions.instance.getHttpsCallable(
functionName: "getRandomNumbers",
);
},
Now as you can see I am trying to get a random number and add it to the firestore database but it is nor working neither showing any error. I am using android studio and I have installed all stuff as suggested in official doc.
The Flutter code you shared doesn't actually call the Cloud Function yet. What you have now merely defines a reference to that Cloud Function. To call it, you'd do something like:
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(
functionName: 'getRandomNumbers',
);
dynamic response = await callable.call();
This is a pretty direct copy from the sample in the documentation on using Cloud Functions in Flutter, so I'd recommend keeping that handy while implementing it.
I noticed that your Cloud Function declaration looks unusual:
exports.getRandomNumbers=functions.https.onCall((num){
The examples in the Firebase documentation follow this pattern:
exports.addMessage = functions.https.onCall((data, context) => {
So the arguments are always data and context, and any parameters you pass from your flutter code will be in the data argument.
For your current code it makes no difference, since you're not using any of the arguments, but I do recommend addressing this going forward.

Error using CLI for cloud functions with IAM namespaces

I'm trying to create an IBM Cloud Function web action from some python code. This code has a dependency which isn't in the runtime, so I've followed the steps here to package the dependency with my code. I now need to create the action on the cloud for this package, using the steps described here. I've got several issues.
The first is that I want to check that this will be going into the right namespace. However though I have several, none are showing up when i do ibmcloud fn namespace list, I just get the empty table with headers. I checked that I was targeting the right region using ibmcloud target -r eu-gb.
The second is that when I try to bypass the problem above by creating a namespace from the command line using ibmcloud fn namespace create nyNamespaceName, it works, but I then check on the web UI, and this new namespace has been created in the Dallas region instead of the London one… I can’t seem to get it to create a namespace in the region that I am currently targeting for some reason, it’s always Dallas.
The third problem is that when I try to follow the steps 2 and 3 from here regardless, accepting that it will end up in the unwanted Dallas namespace, by running the equivalent of ibmcloud fn action create demo/hello <filepath>/hello.js --web true, it keeps telling me I need to target an org and a space. But my namespace is an IAM namespace, it doesn’t have an org and a space, so there are none to give?
Please let me know if I’m missing something obvious or have misunderstood something, because to me it feels like the CLI is not respecting the targeting of a region and not handling IAM stuff correctly.
Edit: adding code as suggested, but this code runs fine locally, it's the CLI part that I'm struggling with?
import sys
import requests
import pandas as pd
import json
from ibm_ai_openscale import APIClient
def main(dict):
# Get AI Openscale GUID
AIOS_GUID = None
token_data = {
'grant_type': 'urn:ibm:params:oauth:grant-type:apikey',
'response_type': 'cloud_iam',
'apikey': 'SOMEAPIKEYHERE'
}
response = requests.post('https://iam.bluemix.net/identity/token', data=token_data)
iam_token = response.json()['access_token']
iam_headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer %s' % iam_token
}
resources = json.loads(requests.get('https://resource-controller.cloud.ibm.com/v2/resource_instances', headers=iam_headers).text)['resources']
for resource in resources:
if "aiopenscale" in resource['id'].lower():
AIOS_GUID = resource['guid']
AIOS_CREDENTIALS = {
"instance_guid": AIOS_GUID,
"apikey": 'SOMEAPIKEYHERE',
"url": "https://api.aiopenscale.cloud.ibm.com"
}
if AIOS_GUID is None:
print('AI OpenScale GUID NOT FOUND')
else:
print('AI OpenScale FOUND')
#GET OPENSCALE SUBSCRIPTION
ai_client = APIClient(aios_credentials=AIOS_CREDENTIALS)
subscriptions_uids = ai_client.data_mart.subscriptions.get_uids()
for sub in subscriptions_uids:
if ai_client.data_mart.subscriptions.get_details(sub)['entity']['asset']['name'] == "MYMODELNAME":
subscription = ai_client.data_mart.subscriptions.get(sub)
#EXPLAINABILITY TEST
sample_transaction_id="SAMPLEID"
run_details = subscription.explainability.run(transaction_id=sample_transaction_id, cem=False)
#Formating results
run_details_json = json.dumps(run_details)
return run_details_json
I know the OP said they were 'targeting the right region'. But I want to make it clear that the 'right region' is the exact region in which the namespaces you want to list or target are located.
Unless you target this region, you won't be able to list or target any of those namespaces.
This is counterintuitive because
You are able to list Service IDs of namespaces in regions other than the one you are targeting.
The web portal allows you to see namespaces in all regions, so why shouldn't the CLI?
I was having an issue very similar to the OP's first problem, but once I targeted the correct region it worked fine.