I want to make a call to my backend (registerSanctumFacebook method) after a facebook Graph request to get user profile info (email), however I'm getting the following error:
await is only allowed within async functions
Pretty self explanatory, the problem is, I don't know how to make graph start method to work with async-await...
const getInfoFromToken = async (token) => {
const PROFILE_REQUEST_PARAMS = {
fields: {
string: 'email',
},
};
const profileRequest = new GraphRequest(
'/me',
{token, parameters: PROFILE_REQUEST_PARAMS},
(error, user) => {
if (error) {
console.log('login info has error: ' + error);
} else {
//this.setState({userInfo: user});
console.log('user:', user);
}
},
);
new GraphRequestManager().addRequest(profileRequest).start();
let response = await registerSanctumFacebook(user.email,user.id);
};
How I call getTokenInfo method:
const loginWithFacebook = async () => {
LoginManager.logInWithPermissions(['email']).then(
login => {
if (login.isCancelled) {
console.log('Login cancelled');
} else {
AccessToken.getCurrentAccessToken().then(data => {
const accessToken = data.accessToken.toString();
console.log('accessToken',accessToken);
getInfoFromToken(accessToken);
});
}
},
error => {
console.log('Login fail with error: ' + error);
},
);
};
As per your problem statement, I think you should add await on this line
await new GraphRequestManager().addRequest(profileRequest).start();
await will only work if the function after await keyword is also async.
or declare registerSanctumFacebook as async
Related
I am trying to download a zip from a url but RNFetchBlob tells me that I have a previous response pending and crashes the application.
This is the function where i call fetch
const downloadFile = async () => {
const dirs = RNFetchBlob.fs.dirs;
const response = await RNFetchBlob.config({
fileCache: true,
appendExt: 'zip',
path: dirs.DownloadDir + '/files/icons.zip',
addAndroidDownloads: {
title: dirs.DownloadDir + '/files/icons.zip',
description: `Download ${dirs.DownloadDir + '/files/icons.zip'}`,
useDownloadManager: false,
notification: false,
},
}).fetch('GET', BASE_URL + 'iconos');
console.log(response.path());
console.log(response);
return response;
};
Here i check the permission
const checkPermission = async () => {
if (Platform.OS === 'ios') {
const response = await downloadFile();
return response;
} else {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: 'Storage Permission Required',
message: 'Application needs access to your storage to download File',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
// Start downloading
const response = await downloadFile();
console.log('Storage Permission Granted.');
return response;
} else {
// If permission denied then show alert
Alert.alert('Error', 'Storage Permission Not Granted');
}
} catch (err) {
// To handle permission related exception
console.log('++++' + err);
}
}
};
And this is my redux reducer
export const getIcons = () => {
return async dispatch => {
dispatch(fetching());
try {
const response = await checkPermission();
dispatch(getResponseSuccess(response));
} catch (error) {
dispatch(getResponseFailure());
}
};
};
When the app execute downloadFile inside checkPermission cath and error that saids
++++Error: canceled due to java.lang.IllegalStateException: cannot make a new request because the previous response is still open: please
call response.close()
I have a Cloud Function deployed to Firebase, and my iOS and Android apps use it fine, all works good. Below is the function deployed.
const admin = require('firebase-admin');
const firebase_tools = require('firebase-tools');
const functions = require('firebase-functions');
admin.initializeApp();
exports.deleteUser = functions
.runWith({
timeoutSeconds: 540,
memory: '2GB'
})
.https.onCall((data, context) => {
const userId = context.auth.uid;
var promises = [];
// DELETE DATA
var paths = ['users/' + userId, 'messages/' + userId, 'chat/' + userId, 'like/' + userId];
paths.forEach((path) => {
promises.push(
recursiveDelete(path).then( () => {
return 'success';
}
).catch( (error) => {
console.log('Error deleting user data: ', error);
})
);
});
// DELETE FILES
const bucket = admin.storage().bucket();
var image_paths = ["avatar/" + userId, "avatar2/" + userId, "avatar3/" + userId];
image_paths.forEach((path) => {
promises.push(
bucket.file(path).delete().then( () => {
return 'success';
}
).catch( (error) => {
console.log('Error deleting user data: ', error);
})
);
});
// DELETE USER
promises.push(
admin.auth().deleteUser(userId)
.then( () => {
console.log('Successfully deleted user');
return true;
})
.catch((error) => {
console.log('Error deleting user:', error);
})
);
return Promise.all(promises).then(() => {
return true;
}).catch(er => {
console.error('...', er);
});
});
function recursiveDelete(path, context) {
return firebase_tools.firestore
.delete(path, {
project: process.env.GCLOUD_PROJECT,
recursive: true,
yes: true,
token: functions.config().fb.token
})
.then(() => {
return {
path: path
}
}).catch( (error) => {
console.log('error: ', error);
return error;
});
}
// [END recursive_delete_function]
How can I execute this script with a button in javascript? A standard .js file locally? I also need to be able to pass in a userId manually.
In my react native app I call it like:
const deleteUser = async () => {
functions().httpsCallable('deleteUser')()
signOut();
}
But in my javascript file (nothing to do with my react native app), I need to pass in a userId and call that same function to delete the user.
There are a number of ways to go about executing a cloud function within your client side application.
Depending on how you have the function setup, you can either pass in a parameter or data via the body in the request.
For example, using express (similar to other frameworks):
// fetch(‘api.com/user/foo’, {method: ‘DELETE’} )
app.delete(‘/user/:uid’, (req, res) => {
const uid = req.params.uid;
// execute function
})
// fetch(‘api.com/user’, {method: ‘DELETE’, body: { uid: foo } } )
app.delete(‘/user’, (req, res) => {
const uid = req.body.uid;
// execute function
})
// fetch(‘api.com/user?uid=foo’, {method: ‘DELETE’} )
app.delete(‘/user’, (req, res) => {
const uid = req.query.uid;
// execute function
})
Full Example:
<button onclick=“deleteUser(uid)”>Delete Me</button>
<script>
function deleteUser(uid) {
fetch(`api.com/user/${uid}`, { method: ‘DELETE’});
// rest of function
}
</script>
Was able to call my firebase function with the following:
userId was accessible like so const { userId } = data; from my function script
async function deleteAccount(userId) {
const deleteUser = firebase.functions().httpsCallable("deleteUser");
deleteUser({ userId }).then((result) => {
console.log(result.data);
});
}
I have a form where i enter an email and it gets ''subscribed'' in a user.json file using a fetch api on node server.My task is to :
upon clicking on the "Unsubscribe" button, implement the functionality for unsubscribing from the community list. For that, make POST Ajax request using http://localhost:3000/unsubscribe endpoint.
I tried to make the function but it wasnt succeseful so i deleted it. Also,i need to do the following :
While the requests to http://localhost:3000/subscribe and
http://localhost:3000/unsubscribe endpoints are in progress, prevent
additional requests upon clicking on "Subscribe" and "Unsubscribe".
Also, disable them (use the disabled attribute) and style them using
opacity: 0.5.
For me ajax requests,fetch and javascript is something new,so i dont know really well how to do this task,if you could help me i'll be happy,thanks in advance.
fetch code for subscribing:
import { validateEmail } from './email-validator.js'
export const sendSubscribe = (emailInput) => {
const isValidEmail = validateEmail(emailInput)
if (isValidEmail === true) {
sendData(emailInput);
}
}
export const sendHttpRequest = (method, url, data) => {
return fetch(url, {
method: method,
body: JSON.stringify(data),
headers: data ? {
'Content-Type': 'application/json'
} : {}
}).then(response => {
if (response.status >= 400) {
return response.json().then(errResData => {
const error = new Error('Something went wrong!');
error.data = errResData;
throw error;
});
}
return response.json();
});
};
const sendData = (emailInput) => {
sendHttpRequest('POST', 'http://localhost:8080/subscribe', {
email: emailInput
}).then(responseData => {
return responseData
}).catch(err => {
console.log(err, err.data);
window.alert(err.data.error)
});
}
index.js from route node server:
const express = require('express');
const router = express.Router();
const FileStorage = require('../services/FileStorage');
/* POST /subscribe */
router.post('/subscribe', async function (req, res) {
try {
if (!req.body || !req.body.email) {
return res.status(400).json({ error: "Wrong payload" });
}
if (req.body.email === 'forbidden#gmail.com') {
return res.status(422).json({ error: "Email is already in use" });
}
const data = {email: req.body.email};
await FileStorage.writeFile('user.json', data);
await res.json({success: true})
} catch (e) {
console.log(e);
res.status(500).send('Internal error');
}
});
/* GET /unsubscribe */
router.post('/unsubscribe ', async function (req, res) {
try {
await FileStorage.deleteFile('user.json');
await FileStorage.writeFile('user-analytics.json', []);
await FileStorage.writeFile('performance-analytics.json', []);
await res.json({success: true})
} catch (e) {
console.log(e);
res.status(500).send('Internal error');
}
});
module.exports = router;
And user.json file looks like this :
{"email":"Email#gmail.com"}
This is my attempt for unsubscribing :
export const unsubscribeUser = () => {
try {
const response = fetch('http://localhost:8080/unsubscribe', {
method: "POST"
});
if (!response.ok) {
const message = 'Error with Status Code: ' + response.status;
throw new Error(message);
}
const data = response.json();
console.log(data);
} catch (error) {
console.log('Error: ' + error);
}
}
It gives the following errors:
Error: Error: Error with Status Code: undefined
main.js:2
main.js:2 POST http://localhost:8080/unsubscribe 404 (Not Found)
FileStorage.js:
const fs = require('fs');
const fsp = fs.promises;
class FileStorage {
static getRealPath(path) {
return `${global.appRoot}/storage/${path}`
}
static async checkFileExist(path, mode = fs.constants.F_OK) {
try {
await fsp.access(FileStorage.getRealPath(path), mode);
return true
} catch (e) {
return false
}
}
static async readFile(path) {
if (await FileStorage.checkFileExist(path)) {
return await fsp.readFile(FileStorage.getRealPath(path), 'utf-8');
} else {
throw new Error('File read error');
}
}
static async readJsonFile(path) {
const rawJson = await FileStorage.readFile(path);
try {
return JSON.parse(rawJson);
} catch (e) {
return {error: 'Non valid JSON in file content'};
}
}
static async writeFile(path, content) {
const preparedContent = typeof content !== 'string' && typeof content === 'object' ? JSON.stringify(content) : content;
return await fsp.writeFile(FileStorage.getRealPath(path), preparedContent);
}
static async deleteFile(path) {
if (!await FileStorage.checkFileExist(path, fs.constants.F_OK | fs.constants.W_OK)) {
return await fsp.unlink(FileStorage.getRealPath(path));
}
return true;
}
}
module.exports = FileStorage;
You should consider using a database for handling CRUD operations on your persisted data. If you must use filestorage, theres a flat file DB library called lowdb that can make working the files easier.
As for preventing duplicate requests, you can track if user has already made a request.
let fetchBtn = document.getElementById('fetch')
let isFetching = false
fetchBtn.addEventListener('click', handleClick)
async function handleClick(){
if (isFetching) return // do nothing if request already made
isFetching = true
disableBtn()
const response = await fetchMock()
isFetching = false
enableBtn()
}
function fetchMock(){
// const response = await fetch("https://example.com");
return new Promise(resolve => setTimeout (() => resolve('hello'), 2000))
}
function disableBtn(){
fetchBtn.setAttribute('disabled', 'disabled');
fetchBtn.style.opacity = "0.5"
}
function enableBtn(){
fetchBtn.removeAttribute('disabled');
fetchBtn.style.opacity = "1"
}
<button type="button" id="fetch">Fetch</button>
I'm attempting to get the value from a JSON response, and then use it in an axios get request. I'm receiving a garbled string, instead of the token from the userData object. How do I return the token through the function, and pass it back to the axios string? The issue seems to revolve around the return function not returning user['access-token']
componentDidMount() {
// console.log("Hello Mounted UserProfile Card!" + this.getToken())
axios
.get(`http://127.0.0.1:3000/api/v1/user_profile/${this.getToken}`)
.then(response => {
this.setState({user: response.data});
console.log("api response " + response.data)
})
.catch(function(error) {
console.log(error);
});
}
async getToken(user) {
try {
let userData = await AsyncStorage.getItem("userData");
let data = JSON.parse(userData);
let user = JSON.parse(data)
return user["access-token"].toString();
} catch(error) {
console.log("Something went wrong", error);
}
}
You are passing this.getToken to axios.get(...), where this.getToken is a method returning a Promise rather than the actual token.
You can first resolve the getToken Promise, then invoke the axios GET request.
componentDidMount() {
this.getToken()
.then(token => axios.get(`http://127.0.0.1:3000/api/v1/user_profile/${token}`))
.then(response => {
this.setState({ user: response.data });
console.log(`API response: ${response.data}`);
})
.catch(error => {
console.error(error);
});
}
async getToken() {
const userData = await AsyncStorage.getItem("userData");
const data = JSON.parse(userData);
const user = JSON.parse(data)
return user["access-token"].toString();
}
This code looks like wrong. getToken method is not calling and in this method, you are not using param user. I am not React expert but i think this can be work;
async componentDidMount() {
// console.log("Hello Mounted UserProfile Card!" + this.getToken())
try {
const token = await this.getToken();
if (token) {
const response = await axios.get(`http://127.0.0.1:3000/api/v1/user_profile/${token}`);
this.setState({user: response.data});
console.log("api response " + response.data)
} else { throw 'token not found'; }
} catch (ex) { console.log(ex); }
}
async getToken() {
try {
let userData = await AsyncStorage.getItem("userData");
let data = JSON.parse(userData);
let user = JSON.parse(data)
return user["access-token"].toString();
} catch(error) {
console.log("Something went wrong", error);
return null;
}
}
I want to create a method that returns an auth header from a token stored in the local storage so it can be called from many ajax, like this:
const fetchUserInfo = (username) => {
const requestString = apiBaseURL + 'access/user_info/'
const form = {username: username}
return axios.post(requestString, form, getAuthHeader())
.then((Response) => {
return {
userInfo: Response.data,
message: null
}
})
.catch((Error) => {
return getErrorMessage();
});
}
In this case, getAuthHeader() is a method that must return a proper authorization header from a token stored in the local storage, so it must use asyncStorage in order to retrieve such token.
This is the code for getAuthHeader:
export const getAuthHeader = async () => {
return await AsyncStorage.getItem('token')
.then ((token) => {
return {
headers: {'Authorization' : 'Token ' + token}
}
})
.catch ((error) => null)
}
The problem is that getAuthHeader() is not returning a header but some other object. I believe I am messing up with the asynchronous nature of asyncStorage, but I can't figure out how to get the value I need.
Yes, you are using async/await and .then (native) together. async/await already handles the .then syntax for you without you having to do it. So you do not need both together it's 1 or the other. Both examples below.
Async/Await:
export const getAuthHeader = async () => {
try {
const token = await AsyncStorage.getItem('token')
if (!token) { return null }
return {
headers: {'Authorization' : 'Token ' + token}
}
} catch(error) { return null }
}
Native
export const getAuthHeader = () => {
return AsyncStorage.getItem('token')
.then((token) => {
if (!token) { return null }
return {
headers: {'Authorization' : 'Token ' + token}
}
})
.catch ((error) => null)
}
also you need to await getAuthHeader in your first method else it will never resolve that value:
const fetchUserInfo = async (username) => {
const requestString = apiBaseURL + 'access/user_info/'
const form = {username: username}
try {
const response = await axios.post(requestString, form, await getAuthHeader())
return {
userInfo: response.data,
message: null
}
} catch(error) {
return getErrorMessage();
}
}