I have written a small contact form, that calls a firebase cloud function to store the request to cloud firestore. Everything works fine, except that after 60seconds the website throws the following error:
Error: deadline-exceeded
I used this reference:https://github.com/firebase/quickstart-js/blob/7d514fb4700d3a1681c47bf3e0ff0fa3d7c91910/functions/functions/index.js
This is my cloud function:
exports.newRequest = functions.https.onCall((data, context) => {
return admin
.firestore()
.collection("requests")
.add(data)
.then(ref => {
console.log(`New request written. ${ref}`)
return ref.id
})
.catch(err => {
throw new functions.https.HttpsError("unknown", error.message, error)
})
})
This is the function call:
const functions = firebase.functions()
const addMessage = functions.httpsCallable(`newRequest`)
addMessage({
name: name,
contact: contact,
message: message,
timestamp: new Date(Date.now()).toLocaleString(),
})
.then(result => {
console.log(`Cloud function called successfully. Ref: ${result.data}`)
})
.catch(error => {
// Getting the Error details.
var code = error.code
var message = error.message
var details = error.details
console.log(code, message, details)
})
Does anybody know how to fix this?
Edit:
Here is the cloud function log:
7:19:33.751 PM newRequest Function execution started
7:19:33.751 PM newRequest Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions
7:19:33.755 PM newRequest Function execution took 5 ms, finished with status code: 204
7:19:33.896 PM newRequest Function execution started
7:19:33.896 PM newRequest Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions
7:19:34.744 PM newRequest New request written. [object Object]
7:19:34.746 PM newRequest Function execution took 851 ms, finished with status code: 200
My setup in detail:
I got a gatsby page, where I init firebase.
import * as firebase from "firebase/app"
const firebaseConfig = {}
useEffect(() => {
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig)
} else {
console.log(firebase.apps)
}
})
I got a contact form react component with the following handleSubmit method.
import * as firebase from "firebase/app"
import "firebase/functions"
const handleSubmit = evt => {
evt.preventDefault()
const addMessage = firebase.functions().httpsCallable(`newRequest`)
addMessage({
name: name,
contact: contact,
message: message,
timestamp: new Date(Date.now()).toLocaleString(),
})
.then(result => {
console.log(`Cloud function called successfully. Ref: ${result.data}`)
})
.catch(error => {
// Getting the Error details.
var code = error.code
var message = error.message
var details = error.details
console.log(code, message, details)
})
resetName()
resetContact()
resetMessage()
}
This is what the chrome dev tools say:
Exception: Error: deadline-exceeded at new HttpsErrorImpl (http://localhost:8000/commons.js:4409:28) at http://localhost:8000/commons.js:4715:20
code: "deadline-exceeded"
details: undefined
message: "deadline-exceeded"
stack: "Error: deadline-exceeded↵ at new HttpsErrorImpl (http://localhost:8000/commons.js:4409:28)↵ at http://localhost:8000/commons.js:4715:20"
__proto__: Error
And this is the promise creator:
/**
* Returns a Promise that will be rejected after the given duration.
* The error will be of type HttpsErrorImpl.
*
* #param millis Number of milliseconds to wait before rejecting.
*/
function failAfter(millis) {
return new Promise(function (_, reject) {
setTimeout(function () {
reject(new HttpsErrorImpl('deadline-exceeded', 'deadline-exceeded'));
}, millis);
});
}
I'm experiencing this problem since several days now and I don't know where it is comming from :(
3 years later! I got this error also on a long running (e.g. 2 mins) firebase function, also using HttpsCallable like the OP
FirebaseError functions/deadline-exceeded
I fixed it by making 2 changes:
(1) increase the firebase function timeout on the server as follows:
exports.myFunction = functions.runWith({
timeoutSeconds: 540
}).https.onCall(async (data: any) => {
return await myFunction(data)
})
OR
alternatively you can manually set the firebase function timeout via the GCP console at https://console.cloud.google.com/functions/list?project=YOUR-PROJECT
click the function name and click edit and you can increase the timeout there
(2) set the HttpsCallableOptions timeout option:
const functions: Functions = getFunctions()
const options: HttpsCallableOptions = { timeout: 530 * 1000 } // slightly less than the 540 seconds BE timeout
const callable: HttpsCallable = httpsCallable(functions, 'flowPlayerExists', options)
const promise: Promise<HttpsCallableResult> = callable({ id })
I had to make BOTH these changes to get it to work!
Is it "useEffect" from React Hooks ?
If yes, your firebase init (in useEffect) is done every render.
If you want that useEffect apply only once (like componentDidMount) you should pass an empty array in second param.
useEffect(() => {
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig)
} else {
console.log(firebase.apps)
}
}, []);
Related
I am attempting to send a test webhook as instructed in this tutorial.
But when I go to do it I get the error seen in the first link, and below:
Test webhook error: 400
Here is my index.ts code & functions I have deployed to firebase functions.
import * as functions from 'firebase-functions';
// const functions = require('firebase-functions');
const stripe = require('stripe')(functions.config().keys.webhooks);
const admin = require('firebase-admin');
admin.initializeApp();
const endpointSecret = functions.config().keys.signing;
exports.events = functions.https.onRequest((request, response) => {
let sig = request.headers["stripe-signature"];
try {
let event = stripe.webhooks.constructEvent(request.rawBody, sig, endpointSecret); // Validate the request
return admin.database().ref('/events').push(event) // Add the event to the database
.then((snapshot: { ref: { toString: () => any; }; }) => {
// Return a successful response to acknowledge the event was processed successfully
return response.json({ received: true, ref: snapshot.ref.toString() });
})
.catch((err: any) => {
console.error(err) // Catch any errors saving to the database
return response.status(500).end();
});
}
catch (err) {
return response.status(400).end(); // Signing signature failure, return an error 400
}
});
exports.exampleDatabaseTrigger = functions.database.ref('/events/{eventId}').onCreate((snapshot, context) => {
return console.log({
eventId: context.params.eventId,
data: snapshot.val()
});
});
How do I fix this and successfully run the test?
My current thinking is that the problem may have something to do with:
How I wrote this line: snapshot: { ref: { toString: () => any; };
Update:
From my testing, this does not appear to be the case.
I don't believe that the 'test webhook' properly signs them; you should use Stripe CLI for this instead.
I am working on a React.js app using Axios for HTTP processing and MobX for state management. I am getting this error, where I sent a test invalid HTTP response that should result in an 404 error. But what I got was a loop of 404 errors keep repeating itself, here's how the console looks:
Here is the method that produces this error. I created a "debugging mode" version:
// for testing
#action loadActivityDos = async (id: string) => {
let activity = this.getActivity(id);
console.log("Step 1: get activity from local activityRegistry");
if (activity) {
this.activity = activity;
console.log("Step 2-1: set local activity to scoped activity");
} else {
this.loadingInitial = true;
try {
activity = await agent.Activities.details(id);
runInAction('getting activity', () => {
this.activity = activity;
this.loadingInitial = false;
})
console.log("Step 2-2: get activity from API in the try block");
} catch(error) {
runInAction('get activity error', () => {
this.loadingInitial = false;
})
console.log("Errored -> error block");
}
}
First, the method hits the try block after failing to get the 'activity' from local array. The first line which awaits for the 'activity' from API throws an Http 404 error, sending to the catch block where the "Errored -> error block" logs in console.
It should end there, but after the "Errored -> error block" log, for an unknown reason it prints the "Step 1: get activity from local activityRegistry", meaning the whole method is ran from beginning again. Then it continues to repeat itself.
Here is the Axios code that communicates with the API, but I couldn't find anything that would cause this behavior:
axios.defaults.baseURL = 'https://localhost:5001/api';
const responseBody = (response: AxiosResponse) => response.data;
const sleep = (ms: number) => (response: AxiosResponse) =>
new Promise<AxiosResponse>(resolve => setTimeout(() => resolve(response), ms));
const requests = {
get: (url: string) => axios.get(url).then(responseBody),
}
const Activities = {
list: ():Promise<IActivity[]> => requests.get('/activities'),
details: (id: string) => requests.get(`/activities/${id}`),
}
I am oblivious why this loop is happening, if anyone could shed some light, I would be very grateful. I'll be happy to provide any other detail.
From where loadActivityDos is called? Do you have a reaction to call it or maybe you use useEffect?
Maybe you have endless reaction loop which reacts to some flag you change
Or maybe useEffect does not have deps array in the end and is called every render.
I have written a firebase Http callable cloud function based on the tutorial here: https://www.youtube.com/watch?v=3hj_r_N0qMs from the firebase team. However, my function is unable to verify the custom claims on a user (me) as 'context.auth' is undefined
I've updated firebase, firebase tools, firebase-functions and admin SDK to the latest versions.
My functions/Index.ts file
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp()
export const addAdmin = functions.https.onCall((data, context) => {
if (context.auth.token.admin !== true) {
return {
error: 'Request not authorized'
};
}
const uid = data.uid
return grantAdminRole(uid).then(() => {
return {
result: `Request fulfilled!`
}
})
})
async function grantAdminRole(uid: string): Promise<void> {
const user = await admin.auth().getUser(uid);
if (user.customClaims && (user.customClaims as any).admin === true) {
console.log('already admin')
return;
}
return admin.auth().setCustomUserClaims(user.uid, {
admin: true,
}).then(() => {
console.log('made admin');
})
}
My app.component.ts code
makeAdmin() {
var addAdmin = firebase.functions().httpsCallable('addAdmin');
addAdmin({ uid: '[MY-USER-ID]' }).then(res => {
console.log(res);
})
.catch(error => {
console.log(error)
})
}
The function executes well if I don't try to access 'context' and I can add a custom claim to this user. However if I try to access context.auth I find the error:
Unhandled error TypeError: Cannot read property 'token' of undefined"
The error message is telling you that context.auth doesn't have a value. As you can see from the API documentation, auth will be null if there is no authenticated user making the request. This suggests to me that your client app does not have a signed-in user at the time of the request to the callable function, so make sure that is the case before invoking the function. If you allow the case where a callable function can be invoked without a signed in user, you will need to check for that case in your function code by checking context.auth before doing work on behalf of that user.
Turns out I wasn't properly integrating AngularFire Functions. I found the solution to my problem here: https://github.com/angular/angularfire2/blob/master/docs/functions/functions.md
I changed my client component code to the following:
import { AngularFireFunctions } from '#angular/fire/functions';
//other component code
makeAdmin() {
const callable = this.fns.httpsCallable('addAdmin');
this.data$ = callable({ uid: '[USERID]' })
.subscribe(resp => {
console.log({ resp });
}, err => {
console.error({ err });
});
}
I've deployed this code to my firebase functions project:
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
admin.initializeApp()
export const getEmail = functions.https.onRequest((request, response) => {
var from = request.body.sender;
admin.auth().getUserByEmail(from)
.then(snapshot => {
const data = snapshot.toJSON()
response.send(data)
})
.catch(error => {
//Handle error
console.log(error)
response.status(500).send(error)
})
})
Which takes in a email parameter that it gets from the user's input on my app. My app's code looks like this:
Functions.functions().httpsCallable("https://us-central1-projectname.cloudfunctions.net/getEmail").call(email) { (result, error) in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain {
//email isnt taken
let code = FunctionsErrorCode(rawValue: error.code)
let message = error.localizedDescription
let details = error.userInfo[FunctionsErrorDetailsKey]
print(code, message, details)
}
// ...
}
if let text = (result?.data as? [String: Any])?["text"] as? String {
// email taken
}
}
When I run the app and when that function is called, it seems to do nothing, no error message is shown and no data has been sent back. What am I missing?
Update: I went to the logs and nothing has happened in there as if the function was never called.
You are actually mixing up HTTP Cloud Functions and Callable Cloud Functions:
You Cloud Function code corresponds to an HTTP one but the code in your front-end seems to call a Callable one.
You should adapt one or the other, most probably adapt your Cloud Function to a Callable one, along the following lines:
exports.getEmail = functions.https.onCall((data, context) => {
const from = data.sender;
return admin.auth().getUserByEmail(from)
.then(userRecord => {
const userData = userRecord.toJSON();
return { userData: userData };
})
});
Have a look at the doc for more details, in particular how to handle errors. The doc is quite detailed and very clear.
I'm trying to send ethereum transaction that sends ERC20 tokens to someone with Ledger Nano S through Node.JS but I'm not able to successfully sign and send this transaction.
First of all, I signed the transaction through the method, signTransaction, of ledgerhq API and then after signing it, I sended it to the main net by using sendSignedTransaction. When I execute below code, Ledger receives request and shows details of a transaction. However, after pressing Ledger's confirm button, the console returns error 'Returned error: Invalid signature: Crypto error (Invalid EC signature)'.
import AppEth from "#ledgerhq/hw-app-eth";
import TransportU2F from "#ledgerhq/hw-transport-u2f";
import TransportNodeHid from "#ledgerhq/hw-transport-node-hid";
import EthereumTx from "ethereumjs-tx"
const Web3 = require('web3');
import { addHexPrefix, bufferToHex, toBuffer } from 'ethereumjs-util';
const web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545'));
var destAddresses = ['0xa6acFa18468786473269Dc1521fd4ff40F6481D9'];
var amount = 1000000000000;
var i=0;
var contract = new web3.eth.Contract([token contract ABI... ], '0x74a...');
const data1 = contract.methods.transfer(destAddresses[0], amount).encodeABI();
const exParams = {
gasLimit: 6e6,
gasPrice: 3e9,
from: '0x1A...',
data : data1,
to: '0x74a...',
value: '0x00',
nonce: "0x0",
chainId: 1,
v: "0x01",
r: "0x00",
s: "0x00"
}
async function makeSign(txParams) {
const tx = new EthereumTx(txParams);
const txHex = tx.serialize().toString("hex");
const signedTransaction = '0x' + txHex;
let transport;
try {
transport = await TransportNodeHid.create();
let eth2 = new AppEth(transport);
const result = await eth2.signTransaction("m/44'/60'/0'/0", txHex).then(result => {
web3.eth.sendSignedTransaction('0x' + txHex)
.then(res => {
console.log(res);
}).catch(err => {
console.log('sendSignedTransaction');
console.log(err);
});
}).catch(err => {
console.log('signTransaction');
console.log(err);
});
txParams.r = `0x${result.r, 'hex'}`;
txParams.s = `0x${result.s, 'hex'}`;
txParams.v = `0x${result.v, 'hex'}`;
return result;
} catch (e) {
console.log(e);
}
}
makeSign(exParams).then(function () {
console.log("Promise Resolved2");
}.catch(function () {
console.log("Promise Rejected2");
});
When I only use signTransaction function, I can confirm the transaction in the ledger device and return txhash on the console. However, ultimately I want to broadcast a transaction to the main net. Could you please give me any idea? I want any feedback. Also, if there are any examples of creating and broadcasting a raw transaction by using the ledger, notice me please.
Your code already sends the transaction to the network. However, just awaiting the "send" promise only gives you the transaction hash, not the receipt. You need to treat it as an event emitter and wait for the 'confirmation' event.
const serializedTx = tx.serialize();
web3.eth.sendSignedTransaction(serializedTx.toString('hex'))
.once('transactionHash', hash => console.log('Tx hash', hash))
.on('confirmation', (confNumber, receipt) => {
console.log(`Confirmation #${confNumber}`, receipt);
})
.on('error', console.error);
To send it to mainnet as you mention, you can either run a local geth node on port 8545 and use your code unchanged, or point web3 at infura or similar.