Get the list of repositories of users from GitHub - node.js

Im trying to achieve the following things in application created from scratch using nodeJs.
Read the list of users from a file in my solution.
Get all the public repositories of those users.
Below is my code
const express = require("express");
const app = express();
const request = require('request');
const fs = require("fs");
var path = require("path");
var rootDir = process.argv.length > 2 ? process.argv[2] : process.cwd();
var filePath = path.join(rootDir, "userList.txt");
const https = require('https');
app.listen(3002, function () {
console.log("Server running on port 3002...");
});
app.get("/getUserRepository", function (req, res, next) {
fs.readFile("myFilePath/myFile.txt", {encoding: "UTF8"}, function (err, userListObject) {
getDataObject(userListObject);
});
});
function getDataObject(userList) {
var userRepoData = [];
var userListArray = userList.split(",");
userListArray.forEach(function (userListObject) {
https.request("https://api.github.com/users/" + userListObject + "/repos", function (res) {
res.setEncoding('utf8');
res.on('data', function (data) {
userRepoData.push(JSON.parse(data));
});
}).end();
});
}
The challenge im facing is, when im making a separate call to get the repo of each user, im getting exception
"Request forbidden by administrative rules. Please make sure your request has a User-Agent header (http://developer.github.com/v3/#user-agent-required)."
Im not finding any example / approach as to where i can add the user-agent.
Also one more thing that i want to know is, where this is the best approach to achieve what i want.

Try this
userListArray.forEach(function (userListObject) {
var options = {
url: "https://api.github.com/users/" + userListObject + "/repos",
headers: {
'User-Agent': 'my node app'
}
};
https.request(options, function (res) {
res.setEncoding('utf8');
res.on('data', function (data) {
userRepoData.push(JSON.parse(data));
});
}).end();
});

As the error says you are missing a header User-Agent. Refactor your code like this
function getDataObject(userList) {
let userRepoData = [];
let userListArray = userList.split(",");
let options = {
host: 'api.github.com',
path: '/users/' + username + '/repos',
method: 'GET',
headers: {'user-agent': 'node.js'}
};
userListArray.forEach(function (userListObject) {
https.request(options, function (res) {
res.setEncoding('utf8');
res.on('data', function (data) {
userRepoData.push(JSON.parse(data));
});
}).end();
});
}
You can read more about User-Agent here.
Basically you need to pass how are you trying to access github api. If you try with browser it automatically sends the user-agent, similar with postman but once writing a script you need to manually pass the user-agent

Related

AWS Lambda async function requires multiple runs to work properly

Essentially the issue is that I have multiple chained promised in an async function. So obviously the code should be waiting for the response before proceeding onto the next part. However with my test case it only works properly after multiple runs. Otherwise it shows the console.log statements sequentially after each run until around 5 runs or so when the final data appears. Sorry if this is an obvious answer or my code is atrocious. I'm rather new to both NodeJS and Async functions. It should also be noted I'm using NodeJS v8.10.
The order is essentially: Call to s3 to get ID in text file -> async function to call api to verify group details -> async function to call api to get other information based on the last function etc.
I've tried messing with the order of the async functions, and searching online for anything like this phenomenon but I was unable to find any examples.
const https = require('https');
var AWS = require('aws-sdk');
var s3 = new AWS.S3();
exports.handler = async (event, context) => {
var groupData;
var groupID;
await fetchGroupID().then(function(fetchedGroupID){
groupID = fetchedGroupID;
return fetchedGroupID;
}).then(function(data){
fetchGroupInfo().then(function(groupInfo){
groupData = groupInfo;
var fetchedGroupID = groupInfo.id;
fetchGroupEvents(fetchedGroupID).then(function(data){
console.log("Group Events: ", data);
});
});
});
console.log(groupData);
};
// Example of one of the async functions
async function fetchGroupEvents(fetchedGroupID)
{
console.log("Fetched GI: ", fetchedGroupID);
console.log("GI: ", groupID);
return new Promise((resolve, reject) => {
const options = {
protocol: 'https:',
hostname: 'api.boomset.com',
port: 443,
path: '/events?group_id=' + groupID,
method: 'GET',
headers: {
'Content-Type' : 'application/json',
'Authorization' : 'Token ****',
'Accept-Encoding': 'gzip, deflate'
}
};
const req = https.request(options, (res) => {
//console.log(`statusCode: ${res.statusCode}`);
res.setEncoding('utf8');
res.on('data', function(data) {
console.log(data);
//var jsonData = JSON.parse(data);
//console.log(jsonData);
resolve(data);
});
});
req.on('error', (error) => {
//console.error(error);
reject(error.message);
});
req.end();
});
};
Ideally this would all be completed in one run.

TypeError: Request path contains unescaped characters, any idea

//route to search (POST http://localhost:8080/api/search)
apiRoutes.post('/search', function(req, res) {
console.log('search');
var query = req.params;
console.log(query);
options = {
protocol : "https:/",
host: "https://api.themoviedb.org",
path: "/3/search/movie?api_key=35f7a26be584f96e6b93e68dc3b2eabd&language=en-US&page=1&include_adult=false&query="+query,
};
var req = https.request(options, function(res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function () {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
});
req.write("{}");
req.end();
})
DOES ANYONE KNOW WHERE THE PROBLEM IS?
I'm trying to do a request to do a research to the api the movie db and get the result back
There are some problems with the code. I have tested it and made it to work.
let options = {
host: "api.themoviedb.org",
path: "/3/search/movie?api_key=35f7a26be584f96e6b93e68dc3b2eabd&language=en-US&page=1&include_adult=false&query="+query.data.replace(' ','%20'),
};
first of all since you are using https module you don't need to specify the protocol nor you need to put it in the url. That's how your options variable should be.
Second you are appending the entire query object to the url which is {} instead you should append a string which will be in one of the key of your query object in my case its query.data
Third if there are spaces in the string Eg: Home Alone you to maintain space and avoid the error we replace the string with %20 which is a escaping character.
Forth Try giving a unique name for https request variable and its response variable in the callback function or it will override the route's req res variables cause your code to not work. Notice how I have used route's res function to send the data back and end the response
Also I am getting the data in req.body and you are using req.params however there are no params defined in your routes. Try going through the documentation for more information
Here is the complete code
apiRoutes.post('/search',function (req, res) {
https = require('https');
var query = req.body;
console.log(query.data);
let options = {
host: "api.themoviedb.org",
path: "/3/search/movie?api_key=35f7a26be584f96e6b93e68dc3b2eabd&language=en-US&page=1&include_adult=false&query="+query.data.replace(' ','%20'),
};
var request = https.request(options, function(response) {
var chunks = [];
response.on("data", function (chunk) {
chunks.push(chunk);
});
response.on("end", function () {
var body = Buffer.concat(chunks);
console.log(body.toString());
res.send(body);
res.end()
});
});
request.end();
});
Hope it helps.

Using Q promises in HTTP requests with NodeJs

I'm trying to make a chain of promises functions which use HTTP requests in NodeJS with Kraken framework.
My code could work in 90% of cases, but if the distant requested server takes time to respond, the code will return an error with undefined values. So I think Q is a good solution to prevent that.
Here's the situation :
We access to a URL with a "code" parameter -> the route controller takes this param to use it in a HTTP POST request -> the response (a token) is stored in a variable and used in an other HTTP GET request -> the response (multiple JSON objects) is stored in variable too -> all variables are stored in a MongoDB.
If functions are not used in this order, of course it fails.
var Q = require('q');
module.exports = function (router) {
router.get('/', function (req, res) {
var codein = req.param('code');
if(codein){
console.log('Provided code: ' + codein+'\n');
getAccessToken(codein).then(function(token){
console.log('Provided AccessToken: ' + token + '\n');
getUsername(token).then(function(userdata){
console.log('Provided Username: ' + JSON.parse(userdata).username + '\n');
storeData(userdata).then(function(msg){
console.log(msg);
res.redirect('/dashboard/' + JSON.parse(userdata).username);
});
});
});
}
else{
console.log('Access Denied, redirecting...');
res.redirect('/');
}
});
};
This method works, but actually didn't resolve the problem, because sometimes variable are undefined again. I think it's my request functions which aren't well made...
Here's an example of the first function with POST request :
var getAccessToken = function(cod){
var def = Q.defer();
var data = querystring.stringify({
client_id:"1234567890",
client_secret:"******",
grant_type:"authorization_code",
redirect_uri:"http://localhost:8000/r/callback",
code:cod
});
var options = {
host: 'domain.server.com',
port: 443,
path: '/api/oauth2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(data)
}
};
var response = "";
var req = https.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
response += chunk;
});
res.on('end', function(){
var json = JSON.parse(response);
var acto = json.access_token;
def.resolve(acto);
});
});
req.write(data);
req.end();
return def.promise;
};
In this case the acto variable can be undefined... So am I using Q in a wrong way ?
EDIT
To understand my problem, let me show you what can I have in my output console (really rare but happens) :
Provided code: 12345678910
Provided Username: user543210
Instead of :
Provided code: 12345678910
Provided AccessToken: 9876543210
Provided Username: user
I think you need to account for 2 scenarios
Where the Twitch API takes time to respond.
The Twitch response cannot be parsed
The code
res.on('end', function(){
var json = JSON.parse(response);
var acto = json.access_token;
def.resolve(acto);
});
Should be modified as:
try {
var json = JSON.parse(response);
var acto = json.access_token;
//check if acto is undefined
if (acto === undefined) {
def.reject('Some error message');
} else {
def.resolve(acto);
}
} catch (error) {
//since the JSON could not be parse
def.reject(error);
}

Nodejs - Frisby testing

Node Script have to call a URL , and get the response and use the some variable of the response to call the next url. is it possible?
in below example, i'm using token in other scripts.
var http = require('http');
var options = {
host: 'url' ,
path: '/path',
method: 'POST'
};
var req = http.request(options, function(res) {
token="hi"; // to be populated by res
});
req.end();
exports.token = token
I think you're trying to export token before it exists. When you assign token = "hi", that happens after the http request finishes and you get the response. What you want to do instead is export a function with a callback which returns your data from the url.
var http = require('http');
var options = {
host: 'url' ,
path: '/path',
method: 'POST'
};
exports.getToken = function (callback) {
http.request(options, function(res) {
callback(res);
});
};
Then in your other module, you would have to call the getToken function passing it a callback function.
//I assume the above file is called tokenFinder.js
var tokenFinder = require('./tokenFinder');
var token;
tokenFinder.getToken(function (data) {
token = data;
});

Get URL Contents in Node.js with Express

How would I go about downloading the contents of a URL in Node when using the Express framework? Basically, I need to complete the Facebook authentication flow, but I can't do this without GETing their OAuth Token URL.
Normally, in PHP, I'd use Curl, but what is the Node equivalent?
var options = {
host: 'www.google.com',
port: 80,
path: '/index.html'
};
http.get(options, function(res) {
console.log("Got response: " + res.statusCode);
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
http://nodejs.org/docs/v0.4.11/api/http.html#http.get
The problem that you will front is: some webpage loads its contents using JavaScript. Thus, you needs a package, like After-Load which simulates browser's behavior, then gives you the HTML content of that URL .
var afterLoad = require('after-load');
afterLoad('https://google.com', function(html){
console.log(html);
});
Using http way requires way more lines of code for just a simple html page .
Here's an efficient way : Use request
var request = require("request");
request({uri: "http://www.sitepoint.com"},
function(error, response, body) {
console.log(body);
});
});
Here is the doc for request : https://github.com/request/request
2nd Method using fetch with promises :
fetch('https://sitepoint.com')
.then(resp=> resp.text()).then(body => console.log(body)) ;
Using http module:
const http = require('http');
http.get('http://localhost/', (res) => {
let rawHtml = '';
res.on('data', (chunk) => { rawHtml += chunk; });
res.on('end', () => {
try {
console.log(rawHtml);
} catch (e) {
console.error(e.message);
}
});
});
rawHtml - complete html of the page.
I just simplified example from official docs.
using Axios is much simpler
const axios = require("axios").default
const response = axios.get("https://google.com")
console.log(response.data)
or
const axios = require("axios").default
const response = axios.get("https://google.com").then((response)=>{
console.log(response.data)
})
for full docs, you can head over Axios Github

Resources