cloud function Not all code paths return a value still I am returning all values possible? - google-cloud-functions

any idea why my cloud function is telling my that (Not all code paths return a value) I put a return on the promise at the end also on the if statement yet it is not working, I read about similar issues tried to solve according to them but I still missing part of the puzzle, the error started showing when I wrapped the whole thing with try-catch.
export const createClient = functions.https.onCall(async(data, context) => {
try {
const snapshotCheckBelong = await db.collection('data- main').doc(data.sharedId).get();
const getClient = snapshotCheckBelong.data() ? .clients.filter((el: any) => el.clientId === data.clientId);
const snapshotCheckExist = await db.collection('data-registered-clients').doc(data.sharedId).get();
const checkClient = snapshotCheckExist.data() ? .clients.filter((el: any) => el.clientId === data.clientId);
if (checkClient[0]) {
return {
result: 'Registered client already exisits.'
};
}
if (getClient[0] ? .clientId === data.clientId && checkClient.length === 0) {
const payload = {
user: 'client',
verifiedEmail: false,
createdAt: admin.firestore.Timestamp.now(),
};
let auth = admin
.auth()
.getUserByEmail(context.auth ? .token ? .email!)
.then((user) =>
admin.auth().setCustomUserClaims(user.uid, {
userType: 'client',
premiumUnitll: admin.firestore.Timestamp.fromDate(snapshotCheckBelong.data() ? .premiumUntill)
})
)
.catch((err: any) => {
console.log(err);
});
let setClientData = db
.collection('data-clients')
.doc(context.auth ? .uid!)
.set(payload)
.catch((err: any) => {
console.log(err);
});
let updateRegisteredClients = db
.collection('data-registered-clients')
.doc(data.sharedId)
.update({
clients: admin.firestore.FieldValue.arrayUnion({
clientAccount: context.auth ? .uid,
clientId: data.clientId,
trainerId: data.sharedId
})
})
.catch((err: any) => {
console.log(err);
});
return Promise.all([auth, setClientData, updateRegisteredClients]);
} else {
return null;
}
} catch {
(err: any) => {
console.log(err);
};
}
});

Your catch blocks are not returning anything. If you handle an error with catch, you have to determine what value gets returned to the caller.
I typically prefer sending the error back to the caller, as shown in the documentation on handling errors in callable cloud functions and on the calling client.

Related

cloud function for sending fcm notifications to a collection of tokens

I am trying to send a notification whenever a new update to my database takes place. I have the onUpdate side working but I am new to FCM and I am stuck at the sending the notification.
The structure of the devices collection is:
+devices/
+tokenId/
-tokenId:njurhvnlkdnvlksnvlñaksnvlkak
-userId:nkjnjfnwjfnwlknlkdwqkwdkqwdd
The function that I have right now that gets stuck with an empty value of token is:
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
const settings = { timestampsInSnapshots: true };
db.settings(settings);
.....
exports.fcmSend = functions.firestore
.document(`chats/{chatId}`).onUpdate((change, context) => {
const messageArray = change.after.data().messages;
const message = messageArray[(messageArray.length-1)].content
if (!change.after.data()) {
return console.log('nothing new');
}
const payload = {
notification: {
title: "nuevo co-lab",
body: message,
}
};
return admin.database().ref(`/devices`)
.once('value')
.then(token => token.val())
.then(userFcmToken => {
console.log("Sending...", userFcmToken);
return admin.messaging().sendToDevice(userFcmToken, payload)
})
.then(res => {
console.log("Sent Successfully", res);
})
.catch(err => {
console.log("Error: ", err);
});
});
I am not able to get the token from the database. It is null or undefined. Can anyone help me with this second part of the function?
Thanks a lot in advance!
Thanks Frank for the tip!
I managed to solve the problem with this code in case anybody needs it:
const payload = {
notification: {
title: "nuevo mensaje de co-lab",
body: message,
}
};
// Get the list of device tokens.
const allTokens = await admin.firestore().collection('devices').get();
const tokens = [];
allTokens.forEach((tokenDoc) => {
tokens.push(tokenDoc.id);
});
if (tokens.length > 0) {
// Send notifications to all tokens.
return await admin.messaging().sendToDevice(tokens, payload);
}else {
return null;
}

ES6 JS Promises - how to avoid conditional nesting

I am trying to write a piece of code using promises, avoiding nesting them but I am stuck in testing the returned results to handle the promises flow ..
Is this pattern workable ??
// set of promise tasks returning values
function doTask1() => {
return apiPromise1()
.then((result1) => {
return result1;
})
}
function doTask2(result1, paramOne) => {
return apiPromise2(result1, paramOne)
.then((result2) => {
return result2;
})
}
function doTask3(result1) => {
return apiPromise3()
.then((result3) => {
return result3;
})
}
function doTask4(result1, paramOne) => {
return apiPromise4()
.then((result4) => {
return result4;
})
}
// main promise to handle the flow of promises according to promises returned results
function getCurrentProcess(paramOne) {
const promises = [];
// how to get the returned result1 to be used by other promises ?
promises.push(doTask1);
if (result1 === 'OK') {
promises.push(doTask2(result1, paramOne));
if (result2 === 'OK') {
promises.push(doTask3(result1));
if (result3 === 'OK') {
promises.push(doTask4(result1, paramOne));
}
}
}
return Promisz.all(promises)
.then(() => {
return 'well done'
});
}
// initial calling function
exports.newJob = functions.https.onRequest((req, res) => {
const paramOne = { ... }
getCurrentProcess(paramOne).then((res) => {
return { status: 200, infos: res };
}, error => {
return {status: error.status, infos: error.message};
}).then(response => {
return res.send(response);
}).catch(console.error);
});
If you want to write promises in more procedural way you need use async/await (ES6). If you need backward compatibility with ES5 you need to use babel or typescript which translate await/async to ES5.
async function getCurrentProcess(paramOne) {
const result1 = await doTask1();
if (result1 === 'OK') {
const result2 = await doTask2(result1, paramOne);
if (result2 === 'OK') {
const result3 = await doTask3(result1);
if (result3 === 'OK') {
await doTask4(result1, paramOne);
}
}
}
return 'well done'
}
Without async/await you need to use promise chain:
doTask1().then((result1)=>{
if (result1 === 'OK') {
...
}
...
})
However it will not produce readable code.
You could write a wrapper function which takes an array of doTaskN as deferred functions:
const conditional = (...fns) => {
if(fns.length === 0) return Promise.resolve();
const [next] = fns;
return next()
.then(() => conditional(...fns.slice(1)));
};
The idea would be to pass in the reference to the doTask functions so that the conditional function executes them. This can be used like:
conditional(doTask1, doTask2, doTask3, doTask4)
.then(() => {
console.log("all done");
})
.catch(() => {
console.log("failed");
});
Here's a full example of how to use it:
const conditional = (...fns) => {
if(fns.length === 0) return Promise.resolve();
const [next] = fns;
return next()
.then(result => {
console.log("task:", result);
if(result === "OK") {
return conditional(...fns.slice(1))
}
});
};
const task1 = (param1, param2) => Promise.resolve("OK");
const task2 = (param1) => Promise.resolve("OK");
const task3 = () => Promise.resolve("failed");
const task4 = () => Promise.resolve("OK");
conditional(() => task1("one", 2), () => task2(1), task3, task4)
.then(() => {
console.log("all done");
})
.catch(() => {
console.log("failed");
});
If you want that your promise return result is used by other promises, you shouldn't use Promise.all() method because it doesn't run methods in the order you want, it just waits for all of the promise methods to complete and returns all results.
Maybe something like promise-array-runner would help?
Maybe you could check if result === 'OK' inside your task method? Or create a Factory which takes care of that.
.then((result1) => {
return result1;
})
is a no-op and should be omitted, but I suppose that real code doesn't have this problem.
This is a use case for async function because they can seamlessly handle this sort of control flow, as another answer suggests. But since async is syntactic sugar for raw promises, it can be written in ES6. Since tasks depend on results of each other, they cannot be processed with Promise.all.
This is same case as this one that uses async.
You can bail out from promise chain by throwing an exception and avoid nested conditions with:
// should be additionally handled if the code is transpiled to ES5
class NoResultError extends Error {}
function getCurrentProcess(paramOne) {
doTask1()
.then(result1 => {
if (result1 !== 'OK') throw new NoResultError(1);
return result1;
})
.then(result1 => ({ result1, result2: doTask2(result1, paramOne) }))
.then(({ result1, result2 }) => {
if (result2 !== 'OK') throw new NoResultError(2);
return result1;
})
// etc
.then(() => {
return 'well done';
})
.catch(err => {
if (err instanceof NoResultError) return 'no result';
throw err;
})
}
Since result1 is used in multiple then callbacks, it could be saved to a variable instead of being passed through promise chain.
Promise chain could become simpler if NoResultErrors were thrown in task functions.
Thanks to all feedbacks !!
All answers are rights... however I voted for CodingIntrigue wtapprer function solution in my case...
1 - As i am using Firebase functions , it's still ES5 , I cannot use sync/await. Using babel or typescript only for Firebase functions will result in much more setup work...
2 - I tested various use cases and this pattern is quite easy to understand with JS level... I am sur that it can be improved later..
so finally I got this running ...
let auth = null;
let myList = null;
const conditional = (...fns) => {
if(fns.length === 0) return Promise.resolve();
const [next] = fns;
return next()
.then(result => {
if(result) {
return conditional(...fns.slice(1));
}
return result;
});
};
const task1 = (param1) => Promise.resolve()
.then(() => {
console.log('TASK1 executed with params: ', param1)
auth = "authObject"
return true;
});
const task2 = (param1, param2) => Promise.resolve()
.then(() => {
console.log('TASK2 executed with params: ', param1, param2)
return true;
});
const task3 = (param1, param2) => Promise.resolve()
.then(() => {
console.log('TASK3 executed with params: ', param1, param2)
myList = "myListObject"
console.log('search for param2 in myList...')
console.log('param2 is NOT in myList task4 will not be executed')
return false;
});
const task4 = (param1) => Promise.resolve()
.then(() => {
console.log('TASK4 executed with params: ', param1)
return true;
});
// FIREBASE HTTP FUNCTIONS ==================
exports.newContactMessage = functions.https.onRequest((req, res) => {
conditional(() => task1("senderObject"), () => task2(auth, "senderObject"), () => task3(auth, "senderObject"), () => task4("senderObject"))
.then((res) => {
return { status: 200, infos: res };
}, error => {
return {status: error.status, infos: error.message};
}).then(response => {
return res.send(response);
}).catch(console.error);
});

How to get data (of my api json) in my object ( Redux, React )?

I not undestand everything with javascript etc, I want to get my data returned by ma action redux but i'have a problem with my code.
const mapStateToProps = state => {
const group = state.groupReducer.group ? state.groupReducer.group : [ ]
return {
group
}
how i can get my data ?
When I try with that:
const mapStateToProps = state => {
const group = state.groupReducer.group.data.data[0] ? state.groupReducer.group.data.data[0] : [ ]
return {
group
}
And my goal is map around group
renderGroup = group => {
return group.map((groups => {
<div key={groups.data.data.id}>
//
</div>
}))
}
Sagas.js
export function* loadApiDataGroup() {
try {
// API
const response = yield
call(axios.get,'http://localhost:8000/api/group');
yield put(loadGroup(response))
} catch (e) {
console.log('REQUEST FAILED! Could not get group.')
console.log(e)
}
}
Action.js
export function loadGroup(data){ return { type: LOAD_GROUP, data }};
export function creatGroup(data){ return { type: CREATE_GROUP, data}};
// reducer
export default function groupReducer( state= {}, action = {}){
switch (action.type){
case LOAD_GROUP:
return {
...state,
group: action.data
}
case CREATE_GROUP:
return {
...state
}
default:
return state
}
thank you to help me
Try
const mapStateToProps = state => ({
group: state.groupReducer.group || []
});
Then you can use this.props.group in the component. Even though you might only want one thing in mapStateToProps, it's usually not directly returned like that.
If group is the response of an API request, you need to unpack data first, this is done in your async action creator (you will want to use redux-thunk or something similar):
const getGroup = () => async (dispatch) => {
dispatch({ type: 'GET_GROUP_REQUEST' });
try {
const { data } = await axios.get('/some/url');
dispatch({ type: 'GET_GROUP_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'GET_GROUP_FAILURE', payload: error });
}
};

State of the redux store is not being updated

I am working on a calculator using MERN stack but my client and server are different projects. React App is running on 3030 and Node Backend on 3000. I am able to retrieve the correct response from Node Backend, but not able to update it to the store, mostly due to the issue with the scope of 'state' or returned data. Below is my code snippet :
const calcReducer = (state = calcState, action) => {
switch(action.type){
case 'ADD_ELEM':
return {
...state,
value: state.value == 0 ? action.text : state.value + action.text
}
case 'CLEAR':
return{
...state,
value: 0
}
case 'EQUAL':
const url = 'http://localhost:3000/calculate';
superagent
.post(url)
.send({ exp : state.value })
.set('Accept', 'application/json')
.end((err,data) => {
if (err)
return state;
else {
console.log(state); //prints the old value of the state
//below prints the correct state value but returning the state from here doesn't work
console.log({
...state,
value : Number(JSON.parse(data.text).result)
})
}
})
return {
...state,
value : VALUE // how can the value be brought here from inside of else loop
}
default:
return state;
}
}
console.log statement inside 'else' prints correctly but no effect if I return state value from there. The place from where I am currently returning 'state' is not working out for me, and the returned state is exactly same as the state before the control came inside the case. Can someone please explain me how to work with the scope as I am new to ES6?
Edit1:
When I try to take the 'async-ness' out of the reducer, and make change as given below:
const mapStateToProps = (state) => {
return{
value: state.value,
btns: state.btns
}
}
const mapDispatchToProps = (dispatch) => {
return{
addElem: (text) => {
dispatch({
type: 'ADD_ELEM',
text
})
},
clear: () => {
dispatch({
type: 'CLEAR'
})
},
equal: (value) => {
console.log(value)
superagent
.post('http://localhost:3000/calculate')
.send({ exp : value })
.set('Accept', 'application/json'))
.end((err,data) => {
dispatch({ type: 'EQUAL', JSON.parse(data.text).result })
})
}
}
}
In this case, code build fails saying:
Module build failed: SyntaxError: Unexpected token (74:2)
72 |
73 | const mapStateToProps = (state) => {
> 74 | return{
| ^
75 | value: state.value,
76 | btns: state.btns
77 | }
There are syntax errors in your mapDispatchToProps, try to well indent your code so it will be more easy to identify them.
const mapDispatchToProps = (dispatch) => {
return {
addElem: (text) => {
dispatch({
type: 'ADD_ELEM',
text
})
},
clear: () => {
dispatch({
type: 'CLEAR'
})
},
equal: (value) => {
console.log(value)
superagent
.post('http://localhost:3000/calculate')
.send({ exp : value })
.set('Accept', 'application/json')
.end((err,data) => {
dispatch({ type: EQUAL, result: JSON.parse(data.text).result })
})
}
};
};
The return statement is within a callback function. So, you are returning only from the callback function. You probably should wrap it in a Promise as follows.
return new Promise((resolve, reject) => {
superagent(...)
...
.end((err, data) => {
if (err)
reject(state);
else {
resolve({
...state,
value : Number(JSON.parse(data.text).result)
});
}
});
});

React Native : render a fetched data structure

I am new to Javascript (NodeJS & JSON data structure) and React native, and I am trying to display some datas on the iOS screen test (react native). I did my best to understand but I am stuck.
I made a GET route on my Express app at /places on localhost:3000 :
router.get('/places', (req, res) => {
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
console.log('Connection established to', url);
var colPlaces = db.collection('places');
colPlaces.find().toArray(function (err,result) {
if (err) {
res.send('error!');
} else if (result.length) {
res.send(result);
} else {
res.send('No entries');
}
})
db.close();
}});
})
It displays a data structure on my browser (seems to be an Array of JSON objects ?)
[
{"_id":"5894fdd694f5d015dc0962bc","name":"The good bar","city":"London"},
{"_id":"5894fdd694f5d015dc0962bd","name":"Alpha bar","city":"Paris"}
]
I am then trying to render it on my native react project :
constructor(props) {
super(props);
var datas = async function getDatas() {
try {
let response = await fetch('http://localhost:3000/places');
let responseJson = await response.json();
return responseJson;
} catch(error) {
console.error(error); }
}
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(datas())
};
}
render() {
return (
<View style={{flex: 1, paddingTop: 22}}>
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>{rowData}</Text>}
/>
</View>
);
}
But all I got is two "0" on my screen.
Can someone help me to find out what is wrong ?
What is/are,
* "result" variable structure ? seems to be an array of JS objects.
* "response" and "responseJSON" variables ?
* "datas()" and dataSource type
Should I parse something ? I am a bit confuse. Thank you.
First, "data" is already plural, there is no "datas". :) Now back to your question, getData is async function, so you cannot just call getData() and expect it to return data immediately. In the constructor, set dataSource to empty list, then:
async function getData() {
try {
let response = await fetch('http://localhost:3000/places');
return response.json(); // this call is not async
} catch(error) {
console.error(error);
}
}
componentDidMount() {
let data = await this.getData();
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({dataSource: ds.cloneWithRows(data)});
}