How to make AJAX request in Hackerrank using JavaScript? - javascript

I open the Hackerrank example test and play around with methods one might use to make an AJAX call. XMLHttpReq, fetch, etc. None of them work; XHR and fetch methods are unavailable.
First fetch:
async function myFetch() {
let response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
let data = await response.json();
console.log(data);
}
Hackerrank throws an error because fetch is not a function. I also tried window.fetch and global.fetch to no avail.
I tried XHR:
function myXHR() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState === 4 && this.status === 200) {
console.log(this.responseText);
// or JSON.parse(this.responseText);
}
};
xmlhttp.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
xmlhttp.send();
}
Hackerrank says XMLHttpRequest is not defined.
Hackerrank is executing Node.JS code, that explains why XHR isn't available, I have to require myself perhaps. Except I can't npm install anything, all I have access to is their little IDE.
How do you make an AJAX call in this platform with JavaScript?

I've passed the HackerRank REST API certification and had the same issue. HackerRank uses a NodeJs environnement to run you code (it's said in the langage selection), so neither XMLHttpRequest nor fetch are available ( as these are Browser only ).
I suggest you use the request npm package, HackerRank allows you to require it.
One downside is that request doesn't support Promises & Async/Await unless you import other packages (which HackerRank doesn't seem to recognize).
Here's what I used :
const request = require('request');
function myFetch(url) {
return new Promise((resolve, reject) => {
request(url, function (error, response, body) {
if(error) reject(error)
else resolve(body)
});
});
}
Note : request package has been recently deprecated, but it will still work well for your use case.

I have given a test in HackerRank recently and I have used the node.js native module for http/https to fetch data from an API. As you can use this without requiring any external libraries.
also, if you need to create promises you can create your own wrapping over the https implementation.
Try using this:
async function fetchData(url) {
const https = require('https');
return new Promise((resolve, reject) => {
https.get(url, (response) => {
let data = '';
response.on('data', (stream) => {
data += stream;
})
response.on('end', () => {
const resolvedData = JSON.parse(data);
resolve(data);
})
}).on('error', (err) => {
reject(err);
})
});
}
async function showData() {
const data = await fetchData('https://jsonmock.hackerrank.com/api/movies?Year=2000');
console.log(data);
}
showData();
this can solve your problem in HackerRank. This example is only given for a get request. For all other Methods please try using the options from https module of Node.js.

Hackerrank currently uses node version 14.x.xx which comes with axios.
All you have to do is scroll to the top and import or require axios, then you can make use of axios.get() or axios.post() as the case may be.

Hackerrank currently uses node version 14.x.xx which comes with Axios
Example for get call:
a) const axios = require('axios');
b) let response = await axios.get('URL here');
In the same way, you can use all HTTP Methods.

let url = 'https://someurl.com/api/article_users?username=username';
const https = require('https');
https.get(url, (res) => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);
res.on('data', (d) => {
process.stdout.write(d);
});
}).on('error', (e) => {
console.error(e);
});
This worked for me.

Related

make async call using native JS in GWT app

I have a GWT app in which I have to include an JS function. So I am using the native interface to use the JS function in my JAVA code.
This is my JS function
function fetchToken() {
return fetch(URL, { method: "POST" })
.then(function(response) {
console.log(response.json());
return response.json();
})
.then(function(data) {
return data.secret;
});
}
But the problem with this is when I receive the Promise response via response.json(), it is still in pending state, so it never goes to line 6. I tried using async but it seems like GWT does not support using async/await.
Is there a way I can use async in GWT or any other way to use JS in GWT other than native interface in which I do not face this issue?
function makeAsyncRequest() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
// Request was successful. Do something with the response.
console.log(xhr.responseText);
}
};
xhr.open("GET", "http://example.com/api/endpoint", true);
xhr.send();
}
I got my mistake, I used response.json() twice in my code, once to log and once to return. I realised I can only use it once in my code. Removing the log fixed my code.
function fetchToken() {
return fetch(URL, { method: "POST" })
.then(function(response) {
return response.json();
})
.then(function(data) {
return data.secret;
});
}

How to structure an asynchronous call to an API?

I'm trying to write some code that makes a call to an API, which will then return some URLs, from each of which I'll then need to make a new call. To avoid nesting loads of callbacks everywhere I've tried using async/await, but it doesn't seem to be working for some reason despite the extremely simple setup I've tried.
I've tried to create two async functions, one of which makes the call to any url and returns the response, and another one which will then call the first function several times depending on the response (i.e. how many urls are returned).
const request = require('request');
init();
async function init() {
const username = "x";
const archiveUrl = "https://someurl.com";
const archiveResponse = await apiCall(archiveUrl)
const archives = archiveResponse.archives;
console.log(archives);
}
async function apiCall(url) {
request(url, { json: true }, (err, res, body) => {
if (err) { return console.log(err); }
console.log(body);
return body;
});
}
This is literally my entire code at the moment and I'm not sure where it's going wrong. The error I'm getting is that it can't read .archives from undefined, and after that error message it's then logging the body from the apiCall function (making me fairly sure that the function isn't awaiting as expected).
That said I can't see what I'm doing wrong. Any tips on general best practice would be much appreciated, I've used async/await before but it's always been hacky and self-taught so I'm sure there's a better way of doing this.
In the apiCall function you are using callback instead of Promise, You need to promisify that function so that const archiveResponse = await apiCall(archiveUrl) actually works:
function apiCall(url) {
return new Promise((res, rej) => {
request(url, { json: true }, (err, res, body) => {
if (err) { return rej(err)}
console.log(body);
return res(body);
});
})
}
If you are using async-await please handle errors by enclosing this in try..catch
Note: Or you can use request-promis or axios they support promise out of the box.
You are close!
let response = await fetch('https://someurl.com');
let data = await response.json();
console.log(response);
decode the JSON body of the response with .json() and it should work

How to cancel http request properly in Node.js?

I need to implement a cancel-able client-side HTTP request in Node.js, without using external libraries. I'm giving a Promise object - cancellationPromise - which gets rejected when the cancellation is externally requested. This is how I know I may need to call request.abort().
The question is, should I be calling request.abort() only if https.request is still pending and response object is not yet available?
Or, should I be calling it even if I already got the response object and am processing the response data, like in the code below? In which case, will that stop any more response.on('data') events from coming?
async simpleHttpRequest(url, oauthToken, cancellationPromise) {
let cancelled = null;
let oncancel = null;
cancellationPromise.catch(error => {
cancelled = error; oncancel && oncancel(error) });
try {
const response = await new Promise((resolve, reject) => {
const request = https.request(
url.toString(),
{
method: 'GET',
headers: { 'Authorization': `Bearer ${oauthToken}` }
},
resolve);
oncancel = error => request.abort();
request.on('error', reject);
request.end();
});
if (cancelled) throw cancelled;
// do I need "oncancel = null" here?
// or should I still allow to call request.abort() while fetching the response's data?
// oncancel = null;
try {
// read the response
const chunks = await new Promise((resolve, reject) => {
response.on('error', reject);
const chunks = [];
response.on('data', data => chunks.push(data));
response.on('end', () => resolve(chunks));
});
if (cancelled) throw cancelled;
const data = JSON.parse(chunks.join(''));
return data;
}
finally {
response.resume();
}
}
finally {
oncancel = null;
}
}
It depends what you want to achieve by aborting a request.
Just a bit of background. HTTP 1 is not able to "cancel" a request it sends it and then waits for the response. You cannot "roll back" the request you did. You need a distributed transaction to do so. (Further reading.) As the MDN developer document states:
The XMLHttpRequest.abort() method aborts the request if it has already been sent. When a request is aborted, its readyState is changed to XMLHttpRequest.UNSENT (0) and the request's status code is set to 0.
Basically you stop the response from being processed by your application. The other application will probably (if you called abort() after it was sent to it) finish its processing anyways.
From the perspective of the question:
The question is, should I be calling request.abort() only if https.request is still pending and response object is not yet available?
TL.DR.: It only matters from the point of view of your application. As I glance at your code, I think it will work fine.

How can I unit test a function that uses promises and event emitters in Node.js?

My question is about unit testing with promises and event emitters in Node.js. I am using the jasmine framework if that matters.
The code below uses the https module of Node.js to send a request to an API. The API will return JSON. The JSON from the API is the "rawData" variable in the code below.
I want to unit test that the function returns JSON (and not a JavaScript object).
I have unsuccessfully tried several approaches to unit testing that aspect of this function:
1) I tried spying on the Promise constructor so that it would return a fake function which would simply return a JSON string.
2) I have tried spying on the .on('eventType', callback) function of EventEmitters in Node.js to fake a function that returns JSON.
My question is: are either of the two approaches above possible and/or recommend for accomplishing my goal? Is there a different approach to isolating the http request and emitting of events from my unit test objective? Do I need to rewrite this function to facilitate easier unit testing?
const https = require('https');
function getJSON() {
return new Promise((resolve, reject) => {
const request = https.get(someConfig);
request.on('response', resolve);
})
.then(msg => {
return new Promise((resolve, reject) => {
let rawData = '';
msg.on('data', chunk => { rawData += chunk });
msg.on('end', () => {
resolve(rawData);
});
});
})
.then(json => {
JSON.parse(json);
return json;
})
}
Is there a reason you want to stick to https for making a request? If not, your code and your testing can both become really simple. I'll give an example using axios.
Http request can look like this
getJSON() {
const url = 'https://httpbin.org/get';
return axios
.get(url)
.then(response => response);
}
and you can stub the get call with Sinon
lab.experiment('Fake http call', () => {
lab.before((done) => {
Sinon
.stub(axios, 'get')
.resolves({ data: { url: 'testUrl' } });
done();
});
lab.test('should return the fake data', (done) => {
const result = requestHelper.getJSON2();
result.then((response) => {
expect(response.data.url).to.eqls('testUrl');
axios.get.restore();
done();
});
});
});
With the existing code, nock would work like this
lab.experiment('Fake http call with nock', () => {
lab.test('should return the fake data', (done) => {
nock('https://httpbin.org')
.get('/get')
.reply(200, {
origin: '1.1.1.1',
url: 'http://testUrl',
});
const result = requestHelper.getJSON2();
result.then((response) => {
const result = JSON.parse(response);
console.log(JSON.parse(response).url);
expect(result.url).to.eqls('http://testUrl');
nock.cleanAll();
done();
});
});
});
Full code is here
I would say that you need to refactor the code a little bit to be more testable.
When I write unit tests for functions I keep below points in mind
You do not need to test for the inbuilt or library modules as they are already well tested.
Always refactor your functions to have very specific reponsibility.
Implementing these two in your example, i would separate the server call in a service module whose sole responsibility is to take url (and configurations, if any) make server calls.
Now, when you do that you get two benefits
1. you have a reusable piece of code which you can now use to make other server calls(also makes your code cleaner and shorter)
Since its a module you can now write seperate tests for that module and take the responsibility of checking whether server calls are made from your current module that uses it.
Now all thats left to test in your getJSON function is to spyOn that service module and use tohaveBeenCalledWith and check that data is properly parsed.You can mock the service to return your desired data.
1 its making a service call
so test for toHaveBeenCalledWith
2 its parsing to JSON
so test for valid/invalid JSON
also test for failures
//no need to test whether https is working properly
//its already tested
const https = require('https');
const service = require("./pathToservice");
function getJSON() {
return service.get(somConfig)
.then(json => {
JSON.parse(json);
return json;
})
}
//its cleaner now
//plus testable
I think you have not succeeded because you're returning directly like that. It should be like:
function getJSON(callback) {
(new Promise((resolve, reject) => {
const request = https.get(someConfig);
request.on('response', resolve);
}))
.then(msg => {
return new Promise((resolve, reject) => {
let rawData = '';
msg.on('data', chunk => { rawData += chunk });
msg.on('end', () => {
resolve(rawData);
});
});
})
.then(json => {
JSON.parse(json);
callback(json);
})
}
// to use this:
getJSON((your_json)=> {
// handling your json here.
})
You can use child_process to spawn a test server to provide JSON API. Example:
const { spawn } = require('child_process');
const expect = chai.expect;
const env = Object.assign({}, process.env, { PORT: 5000 });
const child = spawn('node', ['test-api.js'], { env });
child.stdout.on('data', _ => {
// Make a request to our app
getJSON((foo)=>{
// your asserts go here.
expect(foo).to.be.a('object');
expect(foo.some_attribute).to.be.a('string')
// stop the server
child.kill();
});
});
You can custom your someConfig variable in test environment to point to 'http://127.0.0.1:5000'. your test-api.js file is a simple nodejs script that always response an expected JSON for every request.
Updated unit test example

meteor.call with callback returning undefined

I appreciate that there are many questions on this but I can't seem to find a relevant answer.
I am using a Meteor call with a callback to a method on the server that shrinks an URL via bitly, but although this runs on the server, I am getting a undefined response back on the client.
Any ideas here is the code?
Client
Meteor.call('bitlyShrink','http://test.com', function(error, response) {
console.log(error);
console.log(response);
})
Server
Meteor.methods({
bitlyShrink(longurl) {
check (longurl, String);
const BitlyClient = require('bitly'),
bitly = BitlyClient('token');
bitly.shorten( longurl )
.then( function ( response ) {
console.log(response);
return response;
})
.catch( (error ) => {
return error;
});
}
});
That's a common mistake made while using Promises in Meteor methods.
To make Meteor resolve a Promise and return result to a client you should return the Promise at the end of this method:
Meteor.methods({
bitlyShrink(longurl) {
check (longurl, String);
const BitlyClient = require('bitly'),
bitly = BitlyClient('token');
const bitlyPromise = bitly.shorten(longurl);
// do something else, if needed
return bitlyPromise;
}
});
You should not add .catch(), it will be added by Meteor automatically.
Useful article to read: Using Promises and async/await in Meteor.

Categories

Resources