Axios and App GET/POST requests to external api - javascript

I am new to axios and nodejs/express. Axios calls were made from the React component files themselves, and I decided to move them to a file called "user.service.js" to learn best practices. However they don't work how I expect them to. The following are two scenarios.
For a GET request, this works:
user.service.js:
class UserService {
getDevices() {
return axios.get(process.env.REACT_APP_SERVICE + "/userDevices", {
params: {
access_token: user.access_token,
}
});
}
}
export default new UserService();
server.js:
app.get('/userDevices', (req, res) => {
console.log("grabbing user list of available devices");
axios.get(api_url + "/api/devices", {
headers:
{ 'Authorization': 'Bearer ' + req.query.access_token }
}
).then((response) => {
res.send({ devices: response.data });
}).catch((err) => {
console.log('failed to grab user devices');
res.send('error');
});
});
But this (changing the getDevices function in user.service.js and the header in server.js) doesn't:
user.service.js:
getDevices() {
return axios.get(process.env.REACT_APP_SERVICE + "/userDevices", {
headers: {
authorization: 'Bearer +' user.access_token,
}
});
}
server.js:
app.get('/userDevices', (req, res) => {
console.log("grabbing user list of available devices");
axios.get(api_url + "/api/devices", {
headers: req.headers.authorization
}
).then((response) => {
res.send({ devices: response.data });
}).catch((err) => {
console.log('failed to grab user devices');
res.send('error');
});
});
For a POST request, this doesn't work at all:
user.service.js:
class UserService()
{
addDevice(eid, deviceType, configGroup, name, ip, tenantID) {
return axios.post(process.env.REACT_APP_SERVICE + "/addDevice", {
params :{
eid: eid,
deviceType: deviceType,
configGroup: configGroup,
name: name,
ip: ip,
tenantID: tenantID,
access_token: user.access_token
}
});
}
}
export default new UserService();
server.js:
app.post('/addDevice', (req, res) => {
console.log('Adding new device');
const device = [{
"eid": req.body.eid,
"fields": {
"deviceType": req.body.deviceType,
"configGroup": req.body.configGroup,
"field:name": req.body.name,
"field:ip": req.body.ip
}
}];
axios.post(api_url + "/api/devices", device, {
headers: {
"Authorization": 'Bearer ' + req.query.access_token,
"X-Tenant-ID": req.body.tenantID,
"Content-Type": "application/json"
}
}).then((response) => {
res.send({ status: response.data.message });
}).catch((err) => {
console.log('failed to add device', err);
res.send({ status: 'error' });
});
});
Printing "req.body.tenantID" is undefined. I create a new instance of this class in my React component, and other functions do work. I suspect my calls to "req.body" are not correct, but I'm at a loss right now. Thanks in advance.

Related

How to get value of async function and save it

I'm new in javascript. I've a async function getListBar. Inside getListBar i use return result of getAccount like a input of function fetch( you can see user.access_token) . Code run correct but i don't want call getAccount everytime i use getListBar. So how can i get result of getAccount and save it ?
I've tried many ways but promise very difficult to me , i don't know how to save result of it
async function getAccount() {
try {
let response = await fetch(apiAuthen,
{
method: 'POST',
headers: {
Accept: '*/*',
'Authorization': 'Basic a2VwbGxheTpva2Vwba2VwbGxaQ1YWwjJA==',
'Content-Type': 'application/x-www-form-urlencoded',
'grant_type': 'password',
},
body: qs.stringify({
'grant_type': 'password',
'username': 'abc',
'password': 'abc',
'client_id': 'abc',
})
})
let responseJson = await response.json();
return responseJson.data;
} catch (error) {
console.log(`Error is : ${error}`);
}
}
async function getListBar() {
try {
const user = await getAccount().then(user => { return user });
let response = await fetch(apiBar,
{
headers: {
'Authorization': 'Bearer ' + user.access_token
}
})
let responseJson = await response.json();
return responseJson.data;
} catch (error) {
console.log(`Error is : ${error}`);
}
}
getAccount will return a Promise like this and i want save access_token in it
Promise {_40: 0, _65: 0, _55: null, _72: null}
_40: 0
_55: {access_token: "41b369f2-c0d4-4190-8f3c-171dfb124844", token_type: "bearer", refresh_token: "55867bba-d728-40fd-bdb9-e8dcd971cb99", expires_in: 7673, scope: "read write"}
_65: 1
_72: null
__proto__: Object
If it is not possible to simply store a value in the same scope that these functions are defined, I would create a Service to handle getting the user. Preferably in its own file
AccountService.js
class AccountService {
getAccount = async () => {
if (this.user) {
// if user has been stored in the past lets just return it right away
return this.user;
}
try {
const response = await fetch(apiAuthen, {
method: 'POST',
headers: {
Accept: '*/*',
Authorization: 'Basic a2VwbGxheTpva2Vwba2VwbGxaQ1YWwjJA==',
'Content-Type': 'application/x-www-form-urlencoded',
grant_type: 'password'
},
body: qs.stringify({
grant_type: 'password',
username: 'abc',
password: 'abc',
client_id: 'abc'
})
});
const responseJson = await response.json();
this.user = responseJson.data; // store the user
return this.user;
} catch (error) {
console.log(`Error is : ${error}`);
}
// you should decide how to handle failures
// return undefined;
// throw Error('error getting user :(')
};
}
// create a single instance of the class
export default new AccountService();
and import it where needed
import AccountService from './AccountService.js'
async function getListBar() {
try {
// use AccountService instead
const user = await AccountService.getAccount().then(user => { return user });
let response = await fetch(apiBar,
{
headers: {
'Authorization': 'Bearer ' + user.access_token
}
})
let responseJson = await response.json();
return responseJson.data;
} catch (error) {
console.log(`Error is : ${error}`);
}
}
You will still be calling getAccount each time in getListBar but it will only fetch when AccountService has no user stored.
Now i write in the different way
export default class App extends Component {
constructor() {
super();
this.state = {
accessToken: '',
users: [],
listBar: []
}
}
//Get Account
Check = () => {
getAccount().then((users) => {
this.setState({
users: users,
accessToken: users.access_token
});
}).catch((error) => {
this.setState({ albumsFromServer: [] });
});
}
//Get Account
getAccount() {
return fetch(apiAuthen,
{
method: 'POST',
headers: {
Accept: '*/*',
'Authorization': 'Basic a2VwbGxheTpva2Vwba2VwbGxaQ1YWwjJA===',
'Content-Type': 'application/x-www-form-urlencoded',
'grant_type': 'password',
},
body: qs.stringify({
'grant_type': 'password',
'username': 'abc',
'password': 'abc',
'client_id': 'abc',
})
}).then((response) => response.json())
.then((responseJson) => {
this.setState({
users: responseJson.data,
accessToken: responseJson.data.access_token
});
return responseJson.data
})
.catch((error) => {
console.error(error);
});
}
//Get List Bar
getListBarFromServer() {
return fetch(apiBar, {
headers: {
'Authorization': 'Bearer ' + this.state.accessToken
}
}).then((response) => response.json())
.then((responseJson) => {
console.log(this.getListBarFromServer()) <---- Just run if console
this.setState({ listBar: responseJson.data });
return responseJson.data
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
this.getAccount();
this.getListBarFromServer();
}
render() {
return (
<View style={{ top: 100 }}>
<FlatList data={this.state.listBar} renderItem={({ item }) => {
return (
<View>
<Text>{item.bar_id}</Text>
</View>
)
}}>
</FlatList>
</View>
)
}
}
It's just run when i console.log(this.getListBarFromServer()) .Please explain to me why?

window location replace works only one time

This is very complicated term for me because i am confused how things are done. I have React & Express application where you can upload data and when you upload it will redirect you to other page where your data is displayed, but problem is that it only redirects you one time and when you go back to main page and try to upload file second time it is not uploaded and whole application crashes
here is recipe.js file (part of it) (react)
axios({
method: 'post',
url: '/api/recipes',
config: {headers: {'Content-Type': 'multipart/form-data' }},
data: data
})
.then(res => {
window.location.replace(res.data.location)
})
.catch(err => {
if(err.response){
if(err.response.data.redirect === true){
window.location.replace(err.response.data.location)
}
if(err.response.data.message){
alert(err.response.data.message)
}
}
});
recipe.js (part of it)(expressjs)
const recipe = await Dish.create({
author: user.username,
name: name,
//properties and values
})
return res.status(200).send({
location: '/recipe/' + recipe.name + '/' + recipe._id
})
view-recipe.js (express (part))
componentDidMount(){
const { match: { params } } = this.props;
console.log(`/api/recipe/${params.dishName}/${params.id}`)
axios.get(`/api/recipe/${params.dishName}/${params.id}`)
.then(res => res.data)
.then(data =>{
console.log(data)
}).catch(err=>{
if(err.response.data.message){
alert(err.response.data.message)
}
})
}
view-recipe.js (express)
router.get('/:dishName/:id', async (req, res) => {
try {
const name = req.params.dishName;
const id = req.params.id;
console.log('name ' + name + ' id ' + id)
const recipe = await Dish.findOne({
name: name,
_id: id
}).lean();
if (!recipe) {
return res.status(404).send({
message: 'recipe not found'
})
}
return res.status(200).send({
recipe
})
} catch (err) {
return res.status(500).send({
message: err.message
})
}
})
and finally
index.js (express, for where is guard determinig whether jwt validation token is expired or not and route configurations )
router.use('/api/recipes', guardr, require('./recipe'))
router.use('/api/recipe', require('./view-recipe'))
What is wrong with this code? By the way, before window.location.replace() in recipe.js file (client) i had window.location.href instead and it worked 2 times. it is really confusing for me because i am doing this difficult project for the first time. Thanks!

Axios multiple request on interceptor

I'm using the library axios in my react app.
I'm having a problem with the interceptor.
My question is let say I have three requests happening concurrently and I don't have the token, the interceptor calling the getUserRandomToken three time, I want the interceptor will wait until I'm getting the token from the first request and then continue to the others.
P.S. the token he is with an expiration date so I also checking for it and if the expiration date is not valid I need to create a new token.
This is the interceptor:
axios.interceptors.request.use(
config => {
/*I'm getting the token from the local storage
If there is any add it to the header for each request*/
if (tokenExist()) {
config.headers.common["token"] = "...";
return config;
}
/*If there is no token i need to generate it
every time create a random token, this is a axios get request*/
getUserRandomToken()
.then(res => {
/*add the token to the header*/
config.headers.common["token"] = res;
return config;
})
.catch(err => {
console.log(err);
});
},
function(error) {
// Do something with request error
return Promise.reject(error);
}
);
How about singleton object that will handle the token generations? something similar to this:
const tokenGenerator ={
getTokenPromise: null,
token: null,
getToken(){
if (!this.getTokenPromise){
this.getTokenPromise = new Promise(resolve=>{
/*supposed to be a http request*/
if (!this.token){
setTimeout(()=>{
this.token = 'generated';
resolve(this.token);
},0)
}else{
resolve(this.token);
}
})
}
return this.getTokenPromise;
}
you can reference this same object from the interceptors.
see example: JS FIddle
reference: reference
You can return a Promise from interceptor callback to "wait" until promise fullfiles (this will fit your case). Check out this example:
function axiosCall () {
return new Promise((resolve, reject) => {
Axios.post(URL, {apiKey}).then((response) => {
resolve(response.data.message);
}).catch((error) => {
reject(error);
});
});
}
instance.interceptors.request.use((config) => {
return axiosCall().then((tokenResponse) => {
setWebCreds(tokenResponse);
config.headers.Authorization = `Bearer ${tokenResponse}`;
return Promise.resolve(config)
}).catch(error => {
// decide what to do if you can't get your token
})
}, (error) => {
return Promise.reject(error);
});
More details here: https://github.com/axios/axios/issues/754
Following code doing certain tasks:
Update Token on 401
Make a queue of failed requests while the token is refreshing.
Restore the original request after token refreshing.
Once the peculiar request is given 200, remove it from the queue.
Config.js
import axios from 'axios';
import { AsyncStorage } from 'react-native';
import { stateFunctions } from '../../src/sharedcomponent/static';
const APIKit = axios.create({
baseURL: '',
timeout: 10000,
withCredentials: true,
});
const requestArray = [];
// Interceptor for Request
export const setClientToken = token => {
APIKit.interceptors.request.use(
async config => {
console.log('Interceptor calling');
let userToken = await AsyncStorage.getItem('userToken');
userToken = JSON.parse(userToken);
config.headers = {
'Authorization': `Bearer ${userToken}`,
'Accept': 'application/json',
"Content-Type": "application/json",
"Cache-Control": "no-cache",
}
// console.log('caling ' , config)
return config;
},
error => {
Promise.reject(error)
});
};
// Interceptor for Response
APIKit.interceptors.response.use(
function (response) {
if (requestArray.length != 0) {
requestArray.forEach(function (x, i) {
if (response.config.url == x.url) {
requestArray.splice(i, 1);
}
});
}
return response;
},
function (error) {
const originalRequest = error.config;
requestArray.push(originalRequest);
let reqData = "username=" + number + "&password=" + pin + "&grant_type=password" + "&AppType=2" + "&FcmToken=null";
// console.log('error ' , error);
if (error.message === "Request failed with status code 401" || error.statuscode === 401) {
if (!originalRequest._retry) {
originalRequest._retry = true;
return axios({
method: 'post',
url: '/api/login',
data: reqData,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Cache-Control": "no-cache",
}
})
.then(res => {
let response = res.data;
console.log('successfull Login', response)
if (res.data.StatusCode == 200) {
AsyncStorage.setItem('userToken', JSON.stringify(response.access_token));
stateFunctions.UserId = response.UserId;
stateFunctions.CustomerContactID = response.CustomerContactID;
let obj = {
access_token: response.access_token,
token_type: response.token_type,
expires_in: response.expires_in,
UserId: response.UserId,
CustomerContactID: response.CustomerContactID,
Mobile: response.Mobile,
StatusCode: response.StatusCode
}
AsyncStorage.setItem('logindetail', JSON.stringify(obj));
if (requestArray.length != 0) {
requestArray.forEach(x => {
try {
console.log(x, "request Url");
x.headers.Authorization = `Bearer ${response.access_token}`;
x.headers["Content-Type"] = "application/x-www-form-urlencoded";
APIKit.defaults.headers.common["Authorization"] = `Bearer${response.access_token}`;
APIKit(x)
} catch (e) {
console.log(e)
}
});
}
return APIKit(originalRequest);
}
})
.catch(err => {
console.log(err);
});
}
}
return Promise.reject(error);
}
);
export default APIKit;
Home.js
gettingToken = async () => {
let userToken = await AsyncStorage.getItem('userToken');
userToken = JSON.parse(userToken);
await setClientToken(userToken);
}

Node Js findByIdAndUpdate function not updating in db

Im updating products based on its id in nodejs by creating API and calling them in angular 2 services.
routes/products.js
router.post('/:id', function(req, res, next) {
Products.findByIdAndUpdate(req.params.id, req.body, function (err, post) {
if(err){
res.json({success: false, msg:'Failed updating products'});
} else {
res.json({success: true, msg:'success updating products'});
}
});
});
This is how im passing in postman: POST request
{
"id":"596df0ffcb429a586ef6c0bf",
"category_id": "596ddb165314d15618795826",
"product_name":"Laysssss masala",
"product_price":5,
"product_image":"image1ddd",
"product_desc":"Yummyddsd",
"product_status":"Activedddd"
}
Products seem to be saved in DB.
The same when i call in services under angular and try in browser, i get "Product updated successfully" in broswer , but not reflecting in DB.
component.ts
onSaveConfirm(event) {
if (window.confirm('Are you sure you want to save?')) {
// var prodId = event.newData['_id']
//console.log(event.newData)
this.productService.productUpdate(event.newData).subscribe(data => {
if (data.success) {
console.log('Edited success')
this.flashMessage.show('Product updated successfully', { cssClass: 'alert-success', timeout: 3000 });
this.router.navigate(['/category-items']);
} else {
console.log('failure edited...')
this.flashMessage.show('Something went wrong', { cssClass: 'alert-danger', timeout: 3000 });
this.router.navigate(['/category-items']);
}
});
event.confirm.resolve(event.newData);
} else {
event.confirm.reject();
}
}
product.service.ts
productUpdate(products) {
var prodId = products['_id'];
console.log(products)
let prodUrl = "http://localhost:8080/products/" + prodId
let headers = new Headers();
headers.append('Content-Type', 'application/json');
let prodIdObj = JSON.stringify({ products: products })
return this.http.post(prodUrl, prodIdObj, { headers: headers })
.map((response: Response) => response.json())
.do(data => JSON.stringify(data))
.catch(this.handleError);
}
Maybe your problem here:
In product.service.ts:
productUpdate(products) {
//skipped
let prodUrl = "http://localhost:8080/products/" + prodId
//skipped
}
and products.js
router.post('/:id', function(req, res, next) {
Products.findByIdAndUpdate(req.params.id, req.body, function (err, post) {
if(err){
res.json({success: false, msg:'Failed updating products'});
} else {
res.json({success: true, msg:'success updating products'});
}
});
});
change it to:
router.post('products/:id', function(req, res, next) {
//do something
});

JS convert string to rfc822

I am trying the gmail apis. I've done the auth. Now I want to create a draft. But I am getting this error
{ error:
I20161220-15:53:43.486(4)? { errors: [Object],
I20161220-15:53:43.487(4)? code: 400,
I20161220-15:53:43.488(4)? message: 'Media type \'application/octet-stream\' is not supported. Valid media types: [message/rfc822]' } } }
Gmail api require base64 string with rfc822 standard. I am not sure of any good way to convert a string to rfc822. How do I do that?
I am using meteor for my app and here is my code.
import { Meteor } from 'meteor/meteor'
import { HTTP } from 'meteor/http'
Meteor.startup(() => {
// Meteor.call('createDraft')
Meteor.methods({
'createDraft': function () {
console.log(this.userId)
const user = Meteor.users.findOne(this.userId)
const email = user.services.google.email
console.log(email)
const token = user.services.google.accessToken
const dataObject = {
message: {
raw: CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse('dddd'))
},
headers: {
Authorization: `Bearer ${token}`
}
}
HTTP.post(`https://www.googleapis.com/upload/gmail/v1/users/${email}/drafts`, dataObject, (error, result) => {
if (error) {
console.log('err', error)
}
if (result) {
console.log('res', result)
}
})
}
})
})
Base64 encode the message and replace all + with -, replace all / with _, and remove the trailing = to make it URL-safe:
const rawMessage = btoa(
"From: sender#gmail.com\r\n" +
"To: receiver#gmail.com\r\n" +
"Subject: Subject Text\r\n\r\n" +
"The message text goes here"
).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const dataObject = {
message: {
raw: rawMessage
},
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
};
I just needed to send content type as message/rfc822. Here is the working code. Note that the raw message has something wrong in ts because the draft that is created has empty content. But the draft itself is created successfully.
import { Meteor } from 'meteor/meteor'
import { HTTP } from 'meteor/http'
Meteor.startup(() => {
// Meteor.call('createDraft')
Meteor.methods({
'createDraft': function () {
console.log(this.userId)
// CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse('dddd'))
const user = Meteor.users.findOne(this.userId)
const email = user.services.google.email
console.log(email)
const token = user.services.google.accessToken
const rawMessage = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(
'From: sender#gmail.com\r\n' +
'To: receiver#gmail.com\r\n' +
'Subject: Subject Text\r\n\r\n' +
'The message text goes here'
)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
const dataObject = {
message: {
raw: rawMessage
},
headers: {
'Content-Type': 'message/rfc822',
Authorization: `Bearer ${token}`
}
}
HTTP.post(`https://www.googleapis.com/upload/gmail/v1/users/${email}/drafts`, dataObject, (error, result) => {
if (error) {
console.log('err', error)
}
if (result) {
console.log('res', result)
}
})
}
})
})

Categories

Resources