I have a HTTP POST request I'm making from the frontend using Axios to the Firebase Functions backend. I want to be able to send two requests at the same time to call two functions, createEmaileList and zohoCrmHook. The problem is, when I make a request to both functions at the same time, it gives me the CORS error. When I make a request to individual function, they work perfectly fine. How do I make a request to multiple functions at the same time?
Following is the frontend:
const handleSubmit = e => {
setLoading(true)
e.preventDefault()
axios.all([
axios.post(`${ROOT_URL}/createEmailList`, {
email,
firstName,
lastName
}),
axios.post(`${ROOT_URL}/zohoCrmHook`, {
email,
firstName,
lastName
})
])
.then(axios.spread((emailRes, crmRes) => {
if(emailRes.status===200 || emailRes.status===204 || crmRes.status===200 || crmRes.status===204 || crmRes.status===201){
setLoading(false)
closeModal()
}
}))
.catch(err=> console.log(err));
}
The backend index.js is as follows:
const functions = require('firebase-functions');
const admin = require("firebase-admin")
const serviceAccount = require("./service_account.json");
const createEmailList = require('./createEmailList')
const zohoCrmHook = require('./zohoCrmHook')
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://landing-page.firebaseio.com"
})
exports.zohoCrmHook = functions.https.onRequest(zohoCrmHook)
exports.createEmailList = functions.https.onRequest(createEmailList)
I've imported the cors module and implemented the function as following, but it still only works individually and not both at the same time
createEmailList.js
const admin = require('firebase-admin')
const cors = require('cors')({ origin: true })
module.exports = (req, res) => {
cors(req, res, () => {
if (!req.body.email) {
return res.status(422).send({ error: 'Bad Input'})
}
const email = String(req.body.email)
const firstName = String(req.body.firstName)
const lastName = String(req.body.lastName)
const data = {
email,
firstName,
lastName
}
const db = admin.firestore()
const docRef = db.collection('users')
.doc(email)
.set(data, { merge: false })
.catch(err => res.status(422).send({ error: err }))
return res.status(204).end();
})
}
zohoCrmHook.js
const axios = require('axios');
const functions = require('firebase-functions');
const cors = require('cors')({ origin: true })
// zoho
const clientId = functions.config().zoho.client_id;
const clientSecret = functions.config().zoho.client_secret;
const refreshToken = functions.config().zoho.refresh_token;
const baseURL = 'https://accounts.zoho.com';
module.exports = (req, res) => {
cors(req, res, async () => {
const newLead = {
'data': [
{
'Email': String(req.body.email),
'Last_Name': String(req.body.lastName),
'First_Name': String(req.body.firstName),
}
],
'trigger': [
'approval',
'workflow',
'blueprint'
]
};
const { data } = await getAccessToken();
const accessToken = data.access_token;
const leads = await getLeads(accessToken);
const result = checkLeads(leads.data.data, newLead.data[0].Email);
if (result.length < 1) {
try {
return res.json(await createLead(accessToken, newLead));
} catch (e) {
console.log("createLead error", e);
}
} else {
return res.json({ message: 'Lead already in CRM' })
}
})
}
Update
I've also tried combining the two Firebase Functions into one as following:
exports.myWebHook = functions.https.onRequest(async (req, res) => {
createEmailList(req, res)
zohoCrmHook(req, res)
})
and converting the frontend axios request into one:
const handleSubmit = e => {
setLoading(true)
e.preventDefault()
axios.post(`${ROOT_URL}/myWebHook`, {
email,
firstName,
lastName
})
.then(res => {
if(res.status===200 || res.status===204){
setLoading(false)
closeModal()
}
})
.catch(err=> console.log(err));
}
But, it still gives the same CORS error:
Access to XMLHttpRequest at
'https://us-landing-page.cloudfunctions.net/myWebHook'
from origin 'https://www.website.com' has been blocked by CORS
policy: Response to preflight request doesn't pass access control
check: Redirect is not allowed for a preflight request.
Update2
I've tried to incorporating the CORS module in index.js as following and removed the CORS module from both of the functions.
exports.myWebHook = functions.https.onRequest((req, res) => {
cors(req, res, async () => {
zohoCrmHook(req, res)
createEmailList(req, res)
})
})
Now, the axios request to the server doesn't incur any CORS errors and the myWebHook function gets invoked with no issues, but the neither of zohoCrmHook nor createEmailList function gets invoked.
If we look at the code we see that CORS in not only imported but also invoked with an options object. So I think it is instantiating CORS twice and setting multiple times the same CORS headers, which is known to cause issues.
const cors = require('cors')({ origin: true })
My suggestion is, to instantiate CORS once in the entry point and add the functions as resources to the same CORS instance.
Related
it is showing Unexpected value for STRIPE_SIGNING_SECRET error even after checking it many times in the env file
the terminal shows everything created but it does not reach firebase database I am thinking there is a error in the code
the stripe dashboard also says connected
I am using the forward to local host line in git terminal
webhook code
import { buffer } from "micro";
import * as admin from 'firebase-admin'
//secure a connection to Firebase from backend
const serviceAccount = require('../../../permissions.json');
const app = !admin.apps.length ? admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
: admin.app();
// establish connection to stripe
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_SIGNING_SECRET;
if (typeof endpointSecret !== "string") {
console.error("Unexpected value for STRIPE_SIGNING_SECRET");
// potentially throw an error here
}
const fulfillOrder = async (session) => {
//console.log('Fulfilling order', session)
return app
.firestore()
.collection("user")
.doc(session.metadata.email)
.collection("orders")
.doc(session.id)
.set({
amount: session.amount_total / 100,
amount_shipping: session.amount_total_details.amount_shipping / 100,
images: JSON.parse(session.metadata.images),
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
.then(() => {
console.log(`success: order ${session.id} had been added to db`);
});
};
export default async (req, res) =>{
if(req.method === 'post'){
const requestBuffer = await buffer(req);
const payload = requestBuffer.toString();
const sig = req.headers["stripe-signature"];
let event;
// verify that the event posted came from stripe
try{
event = stripe.webhooks.constructEvent(
payload,
sig,
endpointSecret);
} catch (err) {
console.log('ERROR', err.message)
return res.status(400).send(`Webhook error: ${err.message}`)
}
//handle the checkout event
if (event.type === 'checkout.session.completed') {
const session = event .data.object;
//fulfill the order...
return fulfillOrder(session)
.then(() => res.status(200))
.catch((err) => res.status(400).send(`Webhook error: ${err.message}`));
}
}
};
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
};
firebase rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow write: if false;
allow read: if true;
}
}
}
const endpointSecret = process.env.STRIPE_SIGNNING_SECRET;
Typo: STRIPE_SIGNNING_SECRET
To avoid the next issue, fix the other typo:
const sig = req.headers["stripe-signatur"];
stripe-signature
I'm new to testing in nodejs, I have an express backend split into microservices, and I'm currently trying to test the controller in the User directory.
The controller has a constructor which gets the user service - which is in charge of making DB operations. It's injected usually with awilix. I've been trying to inject my own mock object, but with no luck.
Here's what my userController.test.js looks like:
const request = require('supertest')
const UserController = require('../controllers/userController.js');
const { mockRequest, mockResponse } = require('../utils/interceptor')
const getAllUsers = jest.fn();
const userLogIn = jest.fn();
const getUserById = jest.fn();
const getUserByEmail = jest.fn();
const SearchUsers = jest.fn();
const changePassword = jest.fn();
const ChangeUserPicture = jest.fn();
const addUser = jest.fn();
getUserById.mockReturnValue({
id: 1,
name: 'user',
email: 'user#example.com'
})
userController = new UserController({
getAllUsers,
userLogIn,
getUserById,
getUserByEmail,
SearchUsers,
changePassword,
ChangeUserPicture,
addUser
});
describe('getuserbyid', () => {
test('should fetch a user by id', async () => {
let req = mockRequest();
req.params.id = 1;
const res = mockResponse();
await userController.getUserById(req, res)
.then((res) => console.log(res))
expect(res.mock).toBe({
name: 'user',
email: 'user#example.com'
});
expect(res.mock.calls.length).toBe(1);
})
})
As you can see, I'm passing in my own new object to the UserController constructor, yet when running the tests, I get that it's undefined.
Here's how it looks like in the controller:
async getUserById(req, res) {
try {
return JSON.stringify(await this.userService.getUserById(req));
}
catch (error) {
console.log(`There Was a Problem Getting User. error: ${error.message}`);
return (`Failed to get user, error: ${error.message}`);
}
}
I just get
Failed to get user, error: Cannot read property 'getUserById' of undefined
I am trying to send my variable 'backEndResponse' with its value from my Express.js backend to my React.js Frontend. I am not quite sure how to send a variable from the backend to the frontend. I have searched around and can't find any good resources. I would appreciate any help.
Express.js Backend
function getcookie(req) {
var authCookie = req.headers.cookie;
if (authCookie = req.headers.cookie) {
try {
return authCookie
.split('; ')
.find(row => row.startsWith('Auth='))
.split('=')[1];
} finally {
if (authCookie = result) {
backEndResponse = true
console.log(backEndResponse);
console.log(result);
} else {
backEndResponse = false
console.log(backEndResponse);
console.log(result);
}
}
} else {
}
}
app.get('/auth', (req, res) => {
getcookie(req)
if (backEndResponse) {
res.json(backEndResponse); // OR json({ message: "Authorised" })
} else {
res.json(backEndResponse); // OR json({ message: "Unauthorised" })
}
});
Frontend React.js
const useAuth = () => {
const [data, setData] = useState();
useEffect(() => {
const fetchAuthData = () => {
const result = axios('http://localhost:5000/auth');
console.log(result)
setData(result.data);
};
fetchAuthData()
}, []);
// Logic to check if backEndResponse is true or false
if (data) {
const authorized = {loggedIn: true}
return authorized && authorized.loggedIn;
} else {
const authorized = {loggedIn: false}
return authorized && authorized.loggedIn;
}
}
const ProtectedRoutes = () => {
const isAuth = useAuth();
return isAuth ? <Outlet/> : <Navigate to="/login" />;
}
You won't be able to send a variable directly, rather you will send a payload in a certain shape that best represents the data suited to the applications needs. To send a response payload in an express route use something like the following:
app.get('/auth', (req, res) => {
// do some logic for `backEndResponse`...
res.json(backEndResponse);
});
If you were intending to provide more information in the response such as HTTP headers differing based on the of backEndResponse then you might consider:
app.get('/auth', (req, res) => {
// do some logic for `backEndResponse`...
// send HTTP Ok if true, otherwise Bad Request
// consider handling 400 and/or 500 errors too
if (backEndResponse) {
res.status(200).json(true); // OR json({ message: "Authorised" })
} else {
res.status(401).json(false); // OR json({ message: "Unauthorised" })
}
});
A component fetching the above endpoint would be similar to:
const MyComponent = () => {
const [data, setData] = useState();
useEffect(() => {
const fetchAuthData = async () => {
const result = await axios('http://localhost:5000/auth');
setData(result.data); // true/false OR { message: "Authorised" }
};
fetchAuthData();
}, []);
// display payload
return (<div>{JSON.stringify(data)}</div>)
}
There is an opportunity to refactor the above into a custom hook should you find the need to reuse the functionality across multiple components.
axios request is async function, so you should do like that,
const useAuth = async () => {
try {
const res = await axios.get('http://localhost:5000/auth', {
withCredentials: true
})
return true
} catch (e) {
return false
}
};
Can this anyhow in the feature damage the flow they belong to?
I have a lambda that works behind a API Gateway websocket endpoint.
This simply asks for a clientId and a message payload, query all connections on dynamo for that clientId (multi device realtime dashboard frontend) and updates all interested users.
It's working fine if you test trought "wscat" on command line but it is buggy on real world browser using js websocket api or c# websocket api.
Doest this exceptin has anything to do with it?
const AWS = require("aws-sdk");
let dynamo = new AWS.DynamoDB.DocumentClient();
require("aws-sdk/clients/apigatewaymanagementapi");
const ORDERS_TABLE = "ordersTable";
const successfullResponse = {
statusCode: 200,
body: "everything is alright"
};
module.exports.sendMessageHandler = (event, context, callback) => {
console.log(event);
sendMessageToAllConnectedClientDevices(event)
.then(data => {
console.log("sucesso", data);
callback(null, successfullResponse);
})
.catch(err => {
console.log("erro: ", err);
callback(null, JSON.stringify(err));
});
};
const sendMessageToAllConnectedClientDevices = async event => {
try {
const body = JSON.parse(event.body);
const { clientId } = body;
console.log(
"handler.sendMessageToAllConnectedClientDevices.clientId: ",
clientId
);
const connectionIds = await getConnectionIds(clientId);
return await Promise.all(
connectionIds.Items.map(connectionId => {
send(event, connectionId.connectionId);
})
);
} catch (error) {
console.log("erro sendMessageToAllConnectedClientDevices");
return error;
}
};
const getConnectionIds = async clientId => {
console.log("handler.getConnectionIds.clientId: ", clientId);
const params = {
TableName: ORDERS_TABLE,
// IndexName: "client_gsi",
FilterExpression: "clientId = :cliend_id",
// KeyConditionExpression: "clientId = :cliend_id",
ProjectionExpression: "connectionId",
ExpressionAttributeValues: {
":cliend_id": clientId
}
};
console.log("handler.getConnectionIds.params: ", JSON.stringify(params));
const data = await dynamo.scan(params).promise();
return data;
};
const send = async (event, connectionId) => {
const body = JSON.parse(event.body);
const postData = body.data;
const endpoint =
event.requestContext.domainName + "/" + event.requestContext.stage;
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
apiVersion: "2018-11-29",
endpoint: endpoint
});
const params = {
ConnectionId: connectionId,
Data: postData
};
return await apigwManagementApi.postToConnection(params).promise();
};
ERROR Unhandled Promise Rejection
I think problem is with API Gateway, check how you are handling information passing through to Lambda function (because browser sends some extra information as compared to command line call)
I listen to the chat event of the tmijs library, upon the !overlay chat I want to execute some code. What I want to achieve upon getting that message is:
Fetch the user
Check if the user has enough currency
Deduct currency from the user
Trigger a socket event to my react app
Everything seems to work up until the last bullet point. In my terminal it's shown that my user gets currency (called 'kluiten' in my code) deducted, but all the code that comes after it doesn't get executed.
require('dotenv').config();
const PORT = process.env.PORT || 9000;
class TwitchAPI {
constructor({io}) {
this.io = io;
this.client = new tmi.client(options);
this.client.connect();
this.handleOverlayRequest = this.handleOverlayRequest.bind(this);
this.handleChatMessage = this.handleChatMessage.bind(this);
this.client.on('chat', this.handleChatMessage);
}
handleChatMessage (channel, userstate, message) {
if(message === '!overlay') this.handleOverlayRequest(channel, userstate);
}
async handleOverlayRequest (channel, userstate) {
const requiredKluiten = 5;
const rawFoundUser = await fetch(`http://localhost:${PORT}/api/users/${userstate.username}`);
const foundUser = await rawFoundUser.json();
if(foundUser.instakluiten >= requiredKluiten) {
this.client.action(channel, `${userstate[`display-name`]}, you've got enough instakluiten for this.`);
const method = `PUT`;
const payload = { 'requiredKluiten': requiredKluiten };
const body = JSON.stringify(payload);
const headers = { 'Content-Type': `application/json; charset=utf-8` };
const result = await fetch(`http://localhost:${PORT}/api/users/${userstate.username}/decrementKluiten`, { method, body, headers });
console.log(result);
}
}
}
module.exports = TwitchAPI;
I then have an Express router:
const express = require('express');
const userController = require('../controllers/userController');
const router = express.Router();
router.route('/users/:username/decrementKluiten').put(userController.decrementKluiten);
router.route('/users/:username').get(userController.getUser);
router.route('/overview').get(userController.getOverview);
module.exports = router;
which makes sure the currency gets deducted. What I'm stuck on now is that, after all this has happened, I can't execute any code anymore after the fetch. I found though that I could execute code by resolving the promise in my route, but that feels really dirty and messes up my split up files:
router.route('/users/:username/decrementKluiten').put((req, res) => {
userController.decrementKluiten(req, res).then(x => {
console.log(x);
});
});
Is there a way to wait for my PUT to happen and still execute code after it did?
EDIT
userController.js
const {findChattersPerRole, getUserByUsername, decrementKluiten} = require('../actions');
const find = require(`lodash/find`);
const fetch = require(`isomorphic-fetch`);
const parseJSON = response => response.json();
module.exports = {
getUser: (req, res) => {
const username = req.params.username;
findChattersPerRole()
.then(chattersPerRole => {
const wantedUser = find(chattersPerRole, { username });
getUserByUsername(wantedUser.username)
.then(foundUser => {
if (foundUser) {
res.send(foundUser);
} else {
res.send(`No user has been found`);
}
});
});
},
getOverview: (req, res) => {
fetch(`https://tmi.twitch.tv/group/user/instak/chatters`)
.then(parseJSON)
.then(r => {
return res.json(r);
}).catch(err => {
console.log(err);
});
},
decrementKluiten: (req, res) => {
decrementKluiten(req.params.username, req.body.requiredKluiten);
}
}
actions.js
(Because this contains a lot of code I try to only include the relevant parts for this post, the database calls are done using Sequelize.js)
const decrementKluiten = (username, requiredKluiten) => {
return global.db.Viewer.findOne({
where: { username }
}).then(user => {
return user.decrement({ instakluiten: requiredKluiten });
});
};
module.exports = {
decrementKluiten
};
The issue is likely that you don't respond to the HTTP request in your /users/:username/decrementKluiten route. To solve this, change the exported decrementKluiten method in userController.js-file to this:
decrementKluiten: (req, res) => {
decrementKluiten(req.params.username, req.body.requiredKluiten)
.then(() => res.sendStatus(200))
.catch(() => res.sendStatus(500));
}
Some unrelated pointers to make your code a bit more readable, since you already use async functions in some parts of your code, but in other parts you interface directly with Promises.
The exported part of userController.js could utilize async functions:
module.exports = {
getUser: async (req, res) => {
try {
const username = req.params.username;
let chattersPerRole = await findChattersPerRole();
let wantedUser = find(chattersPerRole, { username });
let foundUser = await getUserByUsername(watnerUser.username);
if (foundUser) {
res.status(200).send(foundUser);
} else {
res.status(404).send('No user has been found');
}
} catch (e) {
res.sendStatus(500);
}
},
getOverview: async (req, res) => {
try {
let r = (await fetch('https://tmi.twitch.tv/group/user/instak/chatters')).json();
res.json(r);
} catch (e) {
res.sendStatus(500);
}
},
decrementKluiten: async (req, res) => {
try {
await decrementKluiten(req.params.username, req.body.requiredKluiten);
res.sendStatus(200);
} catch (e) {
res.sendStatus(500);
}
}
}
I've also added error handling in case something goes wrong, the server responds with a 500 Internal Server Error status code.
Judging by these lines in your TwitchAPI class:
const rawFoundUser = await fetch(`http://localhost:${PORT}/api/users/${userstate.username}`);
const foundUser = await rawFoundUser.json();
I assume you've tried to do const foundUser = await fetch('...').json(). This results in an error, but you can call the retuned value's methods and properties on the same line if you wrap the await expression in parentheses, like this:
const foundUser = await (await fetch('...')).json()`
If its methods does not return a Promise (i.e being synchronous), or you want to access a property, you can do:
const something = (await doSomethingAsync()).someMethod()
const somethingElse = (await doSomethingAsync()).property
I also noticed you're using template literals (backticks, `) for most strings without doing any template interpolation, which could simply be replaced with ' (single-quotes) or " (double-quotes).