I trying to get a download of a json file from an API.
To do that, I need to call 3 endpoints.
http://url.com/export
it returns a json: {"exportLoading":true,"file":"export-20190618-183316.json"}
After that I should call the second endpoint and check the status of this exportation:
http://url.com/export/status
it returns true or false (while server is processing, this endpoint returns true. When it returns false the file is done to make a download.)
So, if the status === false, I can call the last endpoint
http://url.com/download/file_name (I make this request passing the file name - returned from the first request - to download the file.
My question is, how can I check if the second endpoint returns false to make the last request and download the file?
I just did it until the second endpoint.
app.get('/export', function (req, res, next) {
global.fetch = fetch
global.Headers = fetch.Headers;
const headers = new Headers();
const username = 'user';
const password = 'pass';
const URL = 'http://url.com/export'
headers.set('Authorization', 'Basic ' + base64.encode(username + ":" + password));
fetch(URL, {
method: 'GET',
headers: headers,
})
.then(res => res.json())
.then(json => {
fetch("http://url.com/exportl/status", {
method: 'GET',
headers: headers,
}).then(result => ...)
})
.catch(function (error) {
console.log(error)
})
});
You could use a while loop that will call the endpoint until the condition is met:
app.get('/export', async function(req, res, next) {
global.fetch = fetch
global.Headers = fetch.Headers;
const headers = new Headers();
const username = 'user';
const password = 'pass';
const URL = 'http://url.com/export'
headers.set('Authorization', 'Basic ' + base64.encode(username + ":" + password));
fetch(URL, {
method: 'GET',
headers: headers,
}).then(r => r.json)
.then(data => {
// use data here
var status = false
while (!status) {
status = await checkStatus()
}
// third call
})
});
function checkStatus() {
return fetch("http://url.com/exportl/status", {
method: 'GET',
headers: headers,
}).then(r => r.json)
}
Note, I do not know the response from the status, you will have to change the code to accommodate the response.
Related
I'm trying to do a PUT request to an update controller from a react form (Mongoose API). Everything is passing over to the request, except the body. Now, this is my first time using FormData, so I'm almost positive that is where the issue lies, but I can't seem to sort out where the problem is..
The Submit action from the form
const clickSubmit = () => {
// console.log('Values on submit before FormData: ', values) // Shows the state object as expected
let userData = new FormData()
values.name && userData.append('name', values.name)
values.email && userData.append('email', values.email)
values.password && userData.append('password', values.password)
values.about && userData.append('about', values.about)
values.photo && userData.append('photo', values.photo)
update({
userId: match.params.userId
}, {
t: jwt.token
}, userData).then((data) => {
if (data && data.error) {
setValues({...values, error: data.error})
} else {
setValues({...values, 'redirectToProfile': true})
}
})
}
The Helper Method that set up the request
const update = async (params, credentials, user) => {
console.log('The params: ', params) // passes the user ID just fine
console.log('The credentials:', credentials) // passes the JWT just fine
console.log('The user object: ', ...user) // has all the information I'm updating, albeit in an array form that I can't really work with
try {
let response = await fetch('/api/users/' + params.userId, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + credentials.t
},
body: user
})
return await response.json()
} catch (err) {
console.log(err)
}
}
And the controller I've commented out the rest of the logic to remove the clutter while I TS this issue
const update = async (req, res) => {
console.log(req)
const user = await User.findById(req.params.userId)
console.log('user after find: ', user) // returns the user that I want to modify from the database
console.log('body of request: ', req.body) // empty object
}
UPDATE:
I was able to get the FormData into an actual object using Object.fromEntries(user) - but it still won't pass into the request.. I have tried two ways:
const update = async (params, credentials, user) => {
console.log('The params: ', params)
console.log('The credentials:', credentials)
console.log('The user object: ', ...user)
let infoToUpdate = Object.fromEntries(user)
console.log('infoToUpdate: ', infoToUpdate);
try {
let response = await fetch('/api/users/' + params.userId, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + credentials.t
},
body: {
"name": infoToUpdate.name,
"email": infoToUpdate.email,
"about": infoToUpdate.about
}
})
return await response.json()
} catch (err) {
console.log(err)
}
}
And
const update = async (params, credentials, user) => {
console.log('The params: ', params)
console.log('The credentials:', credentials)
console.log('The user object: ', ...user)
let infoToUpdate = Object.fromEntries(user)
console.log('infoToUpdate: ', infoToUpdate);
try {
let response = await fetch('/api/users/' + params.userId, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + credentials.t
},
body: infoToUpdate
})
return await response.json()
} catch (err) {
console.log(err)
}
}
But req.body is still an empty object..
This has been solved, and it was all my flippin computer's fault..
On a whim, I killed node_modules and the package.lock files and reinstalled the deps.. and it started working.. My guess is that bodyParser didn't fully install..
Thank you all for the help.
here is my javascript form handler
where i get data from the form to send it as request to API
import { Store } from './http/requests.js';
$(document).ready(function () {
$('#form_submit').submit(function (e) {
e.preventDefault();
var formData = new FormData(this);
Store(formData);
});
});
js requests file handler
where i use customized post,get functions to send data with options that i provide on it
import { get, post } from '../helper.js';
let pageName = window.location.pathname;
pageName = pageName.slice(1, pageName.length - 5);
export const Store = (value) => {
switch (pageName) {
case 'add_car':
post('user/create_car', value, true, 'multipart/form-data')
.then((res) => {
console.log(res);
return res;
})
.catch((err) => console.log(err));
default:
break;
}
};
then the helper file where i use fetch get,post with option that i receive from "requests.js" file and provide it here
import { Local as loc } from './localStorage.js';
const API_URL = 'http://127.0.0.1:8000/api';
// token if exists in localStorage
const token = loc('get', 'token');
// POST Request
export const post = (
url,
formData,
auth = false,
type = 'application/json',
providedToken = token,
) => {
return fetch(`${API_URL}/${url}`, {
method: 'POST',
body: JSON.stringify(formData),
headers: {
'Content-Type': type,
Authorization: auth ? `Bearer ${providedToken}` : null,
},
})
.then((res) => res.json())
.then((res) => {
console.log(res);
return res;
})
.catch((err) => console.log(err));
};
and finally the Laravel API Cotroller where i tried to debug the issue
public function create_car(Request $request)
{
return (response()->json([
"files" => $_FILES,
"all Request data" => $request,
]));
}
the response i get when i send data from javascript to Laravel API
API gives me back this empty object as a response
it's seems like fetch has a problem ... anyway i just replaced fetch library with axios and everything runs perfectly
here is what i did on helper.js file
// POST Request
export const post = (
url,
formData,
auth = false,
type = 'application/json',
providedToken = token,
) => {
return axios({
method: 'POST',
url: `${API_URL}/${url}`,
data: formData,
headers: {
'Content-Type': type,
Authorization: auth ? `Bearer ${providedToken}` : null,
},
})
.then((res) => {
console.log(res);
return res.data;
})
.catch((err) => console.log(err.data));
};
I'm trying to make a http request based on the documentation at https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow Client Credentials Flow.
I've written
const BASE_URL = 'https://accounts.spotify.com/api/token';
fetch(BASE_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + base64(clientID) + ':' + base64(clientSecret)
},
body: JSON.stringify({'grant_type:client_credentials'})
})
Does this follow what it says to do? I'm confused how to write the body of the post request.
What I ended up doing which works:
async authorize(){
let myHeaders = new Headers();
myHeaders.append("Authorization", `Basic ${my_clientID:clientSecret}`);
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("grant_type", "client_credentials");
const requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
}
let res = await fetch("https://accounts.spotify.com/api/token", requestOptions);
res = await res.json();
return res.access_token;
}
async search(){
const access_token = await this.authorize();
this.setState({access_token});
const BASE_URL = 'https://api.spotify.com/v1/search';
let FETCH_URL = `${BASE_URL}?q=${this.state.query}&type=artist&limit=1`;
const ALBUM_URL = 'https://api.spotify.com/v1/artists';
let myHeaders = new Headers();
myHeaders.append("Authorization", `Bearer ${access_token}`);
const requestOptions = {
method: 'GET',
headers: myHeaders
}
let res = await fetch(FETCH_URL, requestOptions);
res = await res.json();
console.log("ARTIST", res);
}
From the link you have shared, the client credential flow is a client (server-side) that makes a request to the spotify API server. Thus, it is a server-to-server authentication flow (not authorization). You are using the fecth API which is client-side so that means that your implementation should be server-side. If you are using a node.js runtime server-side framework, just look up the http.request API to make a request server-side.
For example, this would be a pure node.js implementation:
const options = {
hostname: 'https://accounts.spotify.com/api/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + base64(clientID) + ':' + base64(clientSecret)
}
};
const req = http.request(options, (res) => {
res.setEncoding('utf8');
// process the data bit by bit or in chunks...
res.on('data', (chunk) => {});
// ...and do something with it when there is no more data in response
res.on('end', () => {
console.log('No more data in response.');
});
});
// handle the error explicitly
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.end();
For me, I'm not sure if this is the case for anyone else, but the spotify api was refusing base64(clientID) + ":" + base64(clientKey), but accepting base64(clientID + ":" + clientKey)
presently I am attempting to make 2 different api calls one after the other within 1 java/nodejs script. It seems after my first function runs successfully, the second one errors with the following:
FetchError: request to failed, reason: socket hang up;
type: 'system',
errno: 'ECONNRESET',
code: 'ECONNRESET'
Below is a code snippet of what I have tried thus far:
const fetch = require("node-fetch");
const formData = require("form-data");
const fs = require("fs");
//const express = require("express");
var apiName = "<LOCAL SYSTEM FILE TO UPLOAD>";
var lookupName = "<LOCAL SYSTEM FILE TO UPLOAD>";
var accessToken = "Bearer <ACCESS TOKEN>";
var url = '<URL API #1>';
var url2 = '<URL API #2>;
var headers = {
'Accept': 'application/json',
'Authorization': accessToken,
};
const form = new formData();
const buffer2 = fs.readFileSync(lookupName);
const buffer = fs.readFileSync(apiName);
const uploadAPI = function uploadAPI() {
form.append("Content-Type", "application/octect-stream");
form.append('file', buffer);
fetch(url, {method: 'POST', headers: headers, body: form})
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
});
};
const uploadLookup = function uploadLookup() {
form.append("Content-Type", "application/octect-stream");
form.append('file', buffer2);
fetch(url2, {method: 'PUT', headers: headers, body: form})
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
});
};
if (!apiName !== true) {
uploadAPI()
} else {}
if (!lookupName !== true) {
console.log("Uploading Lookup File");
uploadLookup()
} else {}
I tried using a "setTimeout" function which does not seem to work as I would have liked it to. My best guess is each API call would need to be it's own separate socket connection? Any help with getting me in the right direction is appreciated.
Promise.all([
fetch(url),
fetch(url2)
]).then(function (res) {
// Get a JSON object from each of the responses
return res.json();
}).then(function (data) {
// do something with both sets of data here
console.log(data);
}).catch(function (error) {
// if there's an error, log it
});
I'm learning nodejs and trying to make an API call. The API uses JWT to authenticate.
I created these functions to sign a token:
function token() {
const payload = {
iat: Math.floor(new Date() / 1000),
exp: Math.floor(new Date() / 1000) + 30,
sub: "api_key_jwt",
iss: "external",
jti: crypto.randomBytes(6).toString("hex")
};
return new Promise((resolve, reject) => {
jwt.sign(payload, privatekey, { algorithm: "RS256" }, function(
err,
token2
) {
if (err) reject(err);
else resolve(token2);
});
});
}
exports.genToken = async function() {
const header = {
"x-api-key": api
};
const data = {
kid: api,
jwt_token: await token()
};
async function authorization(req, res) {
try {
const auth = await rp({
url: authurl,
method: "POST",
headers: header,
body: data
});
res.send(auth.body);
} catch (error) {
res.send(404).send();
}
}
return {
"x-api-key": api,
Authorization: "Bearer " + authorization()
};
};
This works fine. Then I created a function to make the API call:
const token = require("./index").genToken;
const rp = require("request-promise");
exports.getOrderBook = function(res, error) {
const full_url = url + "order_book";
const auth = token();
rp({
url: full_url,
method: "GET",
headers: auth,
body: {
market: "btceur"
},
json: true
})
.then(function(response) {
res(response);
})
.catch(function(err) {
error(err);
});
};
And I call it using Express:
routes.get("/orderbook", async (req, res, next) => {
try {
const book = await orders.getOrderBook();
res.send(book);
} catch (error) {
next(error);
}
});
However, when I call my API, it shows an error in console:
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of
type string or Buffer. Received type object.
I guess the error is something with the token generation, because if I console.log(auth) in the getOrderBook function, it shows Promise { <pending> }, so probably an object is being passed as the jwt token.
Is it really the problem? I tried a lot of different solutions that I found on internet, however the concept of Async/Await is new to me, and I'm having some troubles to figure it out.
Thanks a lot in advance guys!
Since getToken is an anync function, the return is wrapped in a Promise as well so you would need another anync/await:
exports.getOrderBook = async function() {
let response;
try {
const full_url = url + "order_book";
const auth = await token();
response = await rp({
url: full_url,
method: "GET",
headers: auth,
body: {
market: "btceur"
},
json: true
});
} catch (e) {
// handle error
throw e
// or console.error(e)
}
return response;
};
In this line as well Authorization: "Bearer " + authorization(), authorization is returning a promise
const bearer = await authorization()
return {
"x-api-key": api,
Authorization: "Bearer " + bearer
};
For error handling wrap entire thing in try..catch block
exports.genToken = async function() {
try {
const header = {
"x-api-key": api
};
const data = {
kid: api,
jwt_token: await token()
};
async function authorization(req, res) {
let auth;
try {
auth = await rp({
url: authurl,
method: "POST",
headers: header,
body: data
});
// res object not available
// res.send(auth.body);
} catch (error) {
// res object not available, better throw error and handle in your middleware
// res.send(404).send();
}
return auth
}
const bearer = await authorization()
} catch (e) {
// handle error
}
return {
"x-api-key": api,
Authorization: "Bearer " + bearer
};
}