I have the following code.
I'm trying to make an API call (retrieve) passing since (obj.since), therefore, every time I make the call the API does not retrieve all data. However, so far, I haven't found the way to get since from the last record on my database.
var express = require("express");
var article = require("../models/article");
var request = require('request');
article.findOne({}, {since:1, _id:0}, { sort: { 'since' : -1 } }, function (err,obj) {
var **dataString** = `'{"consumer_key":"XXXXX", "access_token":"XXXXXXX", "since":"${obj.since}"}'`;
});
var options = {
url: 'https://xxxxxxxxx.com/v3/get',
method: 'POST',
headers: headers,
body: **dataString**
}
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
let package = JSON.parse(body);
for(var attributename in package.list){
var title = package.list[attributename]["given_title"] ;
var url = package.list[attributename]["given_url"] ;
var newArticle = {title: title, url: url, since: since}
article.create(newArticle, function(error, newlyCreated){
if(error){
console.log(error);
} else {
console.log(newlyCreated);
}
});
}
}
else {
console.log(error);
}
};;
request(options,callback)
How can I make an API call getting the obj.since from the database (MongoDB) and pass it to an object (options)?
You are doing async callback style operation in for loop which is causing this issue. I will change few things
Change findOne to have exec at the end so it returns promise
article.create already returns a promise if no callback specified.
Convert request to a promise style.
Use for..of loop to do async operation.
The code will look like this
var express = require("express");
var article = require("../models/article");
var request = require('request');
function hitApi(dataString) {
return new Promise((resolve, reject) => {
var options = {
url: 'https://xxxxxxxxx.com/v3/get',
method: 'POST',
headers: headers,
body: dataString
}
request(options, error, response, body => {
if (err) {
reject(err);
}
resolve(body);
});
});
}
async function perform() {
const dataString = await article.findOne({}, {since:1, _id:0}, { sort: { 'since' : -1 } }).exec();
const response = await hitApi(dataString);
const package = JSON.parse(response.body);
for (const attributename of package.list) {
var title = package.list[attributename]["given_title"] ;
var url = package.list[attributename]["given_url"] ;
var newArticle = {title: title, url: url, since: since}
const newlyCreated = await article.create(newArticle);
console.log(newlyCreated);
}
}
You can then call perform function. There might be few syntax error but you will get an idea.
Related
I am trying to loop through coordinates of an API endpoint and test each response. When I send the request when it is not nested in for loops it works just fine, however, it doesn't seem to send when nested.
How can I automate the testing of this endpoint with various coordinates?
const request = require('request')
const domain = 'host.local'
const port = '8085'
const url = 'http://' + domain + ':' + port + '/v1/vend/item'
const parameters = {
coordinate: {
x: null,
y: null
},
network: {
user: "config",
role: "admin"
}
}
const x_coordinates = [1,2,3,4,5]
const y_coordinates = [6,7,8,9,10]
let options = {
method: 'post',
body: parameters,
json: true,
url: url
}
for (item in x_coordinates) {
parameters.coordinate.x = parseInt(item) + 1
for (item in y_coordinates.length) {
parameters.coordinate.y = parseInt(item) + 7
sleep(10000)
request(options, (err, res, body) => {
var headers = res.headers
var statusCode = res.statusCode
})
}
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break
}
}
}
Alternative promise method
for(let i=0; i<x_coordinates.length; i++) {
body.coordinate.x = i
for(let j=0; j<y_coordinates.length; j++) {
body.coordinate.y = j
let options = {
url: 'http://' + domain + ':' + port + '/v1/vend/item',
method: 'post',
json: true,
body: body
}
ps.push(rp(options))
}
}
Promise.all(ps)
.then((results) => {
console.log(results)
})
.catch(err => {
console.log(err)
})
This implementation of promises sent all the requests at once. They need a delay between them. Ideally, once the first request gets a response, the second is sent.
I like to use a little helper function called chainAsync:
https://github.com/30-seconds/30-seconds-of-code#chainasync
Here it is written a bit less densely:
function chainAsync(arrayOfFunctions){
let currentFunctionIndex = 0
const lastFunction = arrayOfFunctions[arrayOfFunctions.length - 1]
goToNextFunction()
function goToNextFunction(){
const currentFunction = arrayOfFunctions[currentFunctionIndex]
if(currentFunction == lastFunction){
currentFunction()
}else{
currentFunction(goToNextFunction)
currentFunctionIndex += 1
}
}
}
You can use it like this:
chainAsync([
function(goToNextFunction){
request(options, (err, res, body)=>{
// Handle the response. Then...
goToNextFunction()
})
},
function(goToNextFunction){
request(options, (err, res, body)=>{
// Handle the response. Then...
goToNextFunction()
})
},
function(){
request(options, (err, res, body)=>{
// Handle the response. Then...
// ...don't go to next function, since there isn't a next function!
})
}
])
This way you have control over the order in which these asynchronous functions take place.
Here's one way of using it to address your use-case:
const requestsToExecute = []
x_coordinates.forEach(x=>{
y_coordinates.forEach(y=>{
const currentRequest = function(goToNextRequest){
const requestOptions = {
url: 'http://host.local:8085/v1/vend/item',
method: 'POST',
json: true,
body: {
coordinate: {x, y},
network: {
user: 'config',
role: 'admin'
}
}
}
request(requestOptions, (err, response, body)=>{
// Handle the response, then...
// ...if there's another request...
if(goToNextRequest){
// ...use setTimeout to wait 1e7 seconds before going to the next request
setTimeout(goToNextRequest, 1e7)
}
})
}
requestsToExecute.push(currentRequest)
})
})
chainAsync(requestsToExecute)
I am currently trying to work with the API from http://www.pdflayer.com, however I am having issues with providing the api_key through a post request with Axios.
My code looks like this:
var config = require('./../config');
var axios = require('axios');
var fs = require('fs');
const BASE_URL = 'http://api.pdflayer.com/api/convert';
module.exports = {
createQuotePdf() {
var data = {
document_url: 'https://www.apple.com',
access_key: config.pdflayer_acccess_key,
page_size: 'A4',
test: '1'
}
axios.post(BASE_URL, data)
.then((data) => {
console.log(data);
fs.writeFile('./download.pdf', data.body, function(err) {
if (err) console.log('error: ', err);
})
});
}
};
However, every time I make the request, it is saying that I did not provide the api key, even though I specified.
It would be great if someone could help me.
Best regards
For anybody having the same issue, here is the working solution:
var config = require('./../config');
var request = require('request');
var BASE_URL = 'http://api.pdflayer.com/api/convert';
var ACCESS_KEY = '?access_key=' + config.pdflayer_acccess_key;
var API_URL = BASE_URL + ACCESS_KEY;
module.exports = {
createPdf() {
var formData = {
document_html: `<html><body>Hello World</body></html>`
}
request.post({url: API_URL, formData: formData, encoding: null}, function optionalCallback(err, httpResponse, body) {
if (err) {
console.log(err);
} else {
// Here you can save the file or do anything else with it
console.log(body);
}
});
}
};
Have you tried appending it directly to your BASE_URL since that seams to be the way they are building it
BASE_URL = ('http://api.pdflayer.com/api/convert?access_key=', YOUR_ACCES_KEY)'
I'm trying to send multiple HTTP requests from my node.js server (using the 'request' package) in a for loop.
The idea is to basically have an array of URLs, send a HTTP get for each one, and store the response in an array called 'responseArray'.
When I try this, I get 'undefined', but I know the requests are working if I log them to the console inside the for loop.
function apiCalls() {
var URLs = [‘URL1’, ‘URL2’, ‘URL3’];
var responseArray = []
for (var i = 0; i < URLs.length; i++) {
request({
url: URLs[i],
method: 'GET',
headers: {
'Connection': 'close'
},
}, function(error, response, body) {
if (error) {
console.log(error);
} else {
responseArray.push(String(response.body));
console.log(responseArray) // this is showing me all the data in the array correctly
return responseArray;
}
}) //end request function
} //end for loop
console.log(responseArray)
} //end apiCalls function
apiCalls()
So after looking at a few different solutions here on stack overflow and elsehwere I tried using promises. I've never used them before and based this off the google example
Promise:
var apiCalls = new Promise(function(resolve, reject) {
var URLs = [‘URL1’, ‘URL2’, ‘URL3’];
var responseArray = [];
for (var i = 0; i < URLs.length; i++) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
request({
url: URLs[i],
method: 'GET',
headers: {
'Connection': 'close'
},
}, function(error, response, body) {
if (error) {
console.log(error);
} else {
resolve(responseArray.push(String(response.body))
}
}) //end request
} //end for loop
})
apiCalls.then(function(result) {
console.log('this is calling the promise')
console.log(result)
}, function(err) {
console.log(err)
});
I always get an empty array when I try to log the responseArray after the for loop. Alternatively - I get 'undefined' if I try to assign the returned array to a variable like so:
var gimmeAllResponses = apiCalls();
console.log(gimmeAllResponses); //returns 'undefined'
Can anyone show me where I'm going wrong? how do I updated the 'responseArray' data after the for loop has finished?
This is a little bit off, since this requires the alternative package, request-promise.
You're resolving many times. Since you're using Node.js, it's very likely that ES6 features are available. Use Array.prototype.map() and Promise.all():
var rp = require('request-promise');
var URLs = [‘URL1’, ‘URL2’, ‘URL3’];
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var responseArray = Promise.all(URLs.map((url) => rp({
uri: url,
method: 'GET',
headers: {
'Connection': 'close'
}
}).then((error, response, body) => {
if (error) {
console.log(error);
} else {
return String(response.body);
}
})));
I'm trying to invoke a REST call and return a promise so I can manipulate the data afterwhich.
var self = this;
var returnPromise;
returnPromise = self.httpService.testService();
returnPromise.then(function(result){
console.log(result);
});
My REST service is located in another file which has the following:
testService() {
console.log("start of testService");
var request = require('request');
var user = "test";
var ipadd = "127.0.0.1";
request({
url: 'https://' + 'LocalHost' + '/URLOFWEBSERVICE',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
userID: user,
userIpAddress: ipadd
}
}, function(error, response, body) {
if (error) {
console.log(error);
} else {
console.log(response.statusCode, body);
var newPro = new Promise(function(fulfill, reject) {
fulfill(body);
});
console.log(newPro);
return newPro;
}
});
}
I'm able to print out response.stateCode , body( REST result) and the "invoked fulfill" console is printed out.
The problem lies with the
returnPromise.then
in the first page, in which ".then" returns me undefined.
At first I thought that it might be the promise has been called before the REST returns me a response. So, I thought of doing a timeout function to test.
setTimeout(
function(){
console.log(returnPromise);
}
, 5000);
But even so, returnPromise returns me "undefined".
If you expect a function to return a promise, it should return a promise
your testService does not return anything
this does:
testService() {
console.log("start of testService");
var request = require('request');
var user = "test";
var ipadd = "127.0.0.1";
return new Promise(function(fulfill, reject) {
request({
url: 'https://' + 'LocalHost' + '/URLOFWEBSERVICE',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
userID: user,
userIpAddress: ipadd
}
}, function(error, response, body) {
if (error) {
// reject the promise, handle with .catch
reject(error);
} else {
console.log(response.statusCode, body);
fulfill(body);
}
});
});
}
That can be called as
// var self = this; // why? not needed in the code shown
// var returnPromise; // why? it's not needed in the code shown
this.httpService.testService()
.then(function(result) {
console.log(result);
})
.catch(function(err) {
// handle your errors here
});
I'm still learning how to make calls with using Express. Single asynchronous calls are no problem, but now I have an example where I want to feed the result of one call into the next one.
My current code can be seen below, a really messy way that includes a forloop like function, a foreach and a Timeout. I'd like to learn the proper way to do this. Preferably something that scales.
The first call fills up a result object getWorkbooksResponse with a list of workbooks (incl. workbook id's and workbook names). The second part fires of a getViews call for each workbook in that list. The function checkResponse sorts through the views and puts them in alphabetical order by name.
What is the proper way to tie together 2 request.post calls?
I've been looking at next(), bluebird, async,... but some examples would definitely help.
var express = require('express');
var request = require('request');
var router = express.Router();
//initialize values
var workbookId = -1;
router.get('/workbooks/views', function(req, res) {
var workgroup = req.query.workgroup;
var authToken = req.query.auth_token;
var serverUrl = req.query.server_url;
//Assemble body for POST request...
var requestedBody = {
method: 'getWorkbooks',
params: {
page: {
startIndex: 0,
maxItems: 999
},
filter: {
operator: 'and',
clauses: []
}
}
};
//Send POST request...
request.post({
url: 'https://' + serverUrl + '/vizportal/api/web/v1/getWorkbooks',
body: JSON.stringify(requestedBody),
headers: {
'Cookie': 'workgroup_session_id=' + workgroup + '; XSRF-TOKEN=' + authToken,
'X-XSRF-TOKEN': authToken
}
}, function(err, response, body) {
body = JSON.parse(body);
var result = body.result;
if (result.errors) {
return res.json({
http_code: 401
});
} else {
getWorkbooksResponse = result;
var getViewsWorkbooksResponse = [];
var forloop = function(i) {
if (i < getWorkbooksResponse.totalCount) {
workbookId = getWorkbooksResponse.workbooks[i].id;
var workbookName = getWorkbooksResponse.workbooks[i].name;
request.post({
url: 'https://' + serverUrl + '/vizportal/api/web/v1/getViews',
body: JSON.stringify({
method: 'getViews',
params: {
page: {
startIndex: 0,
maxItems: 999
},
filter: {
operator: 'and',
clauses: [{
operator: 'eq',
field: 'workbookId',
value: workbookId
}]
}
}
}),
headers: {
'Cookie': 'workgroup_session_id=' + workgroup + '; XSRF-TOKEN=' + authToken,
'X-XSRF-TOKEN': authToken
}
}, function(err, response, body) {
body = JSON.parse(body);
var result = body.result;
if (result.errors) {
response = {
http_code: 401
};
} else {
result.views.forEach(function(view) {
view.workbookName = workbookName;
getViewsWorkbooksResponse.push(view);
});
}
});
forloop(i + 1);
} else {
var checkResponse = function() {
if (getViewsWorkbooksResponse) {
//Alphabetize Response array on view name
getViewsWorkbooksResponse.sort(function(a, b){
return a.name.localeCompare(b.name);
});
return res.json({
http_code: 200,
response: getViewsWorkbooksResponse
});
}
};
setTimeout(checkResponse, 1000);
}
};
if (getWorkbooksResponse.totalCount) {
forloop(0);
}
}
});
});
module.exports = router;
With promises it would be the simplest way:
You wrap your request.post calls in
new Promise((resolve,reject)=>
{ ...
request.post(...,function(err,response, body){
if (result.errors){reject(/* error info here */)}
else {resolve(/* data goes here */)}
}
});
And then use
Promise.all[promise1,...,promiseN].then(function(result){
//here is where you gonna do things after all requests are done
})