I am working on an Express.js app. The current feature is creating an appointment with a post request and getting and saving data from third party API, then sending updated API data in the subsequent request. The feature is fully working but in the test, the function to get API data is not getting called.
Route to create appointment:
app.post('/schedule', requestHandler.postSchedule);
The request handler for creating appointment:
requestHandler.postSchedule = function (req, res) {
let appointment = {
// posted data
};
new Appointment(appointment)
.save()
.then(newAppointment => {
if(req.body.cityName && req.body.cityName !== '') {
console.log('req.body.cityName', req.body.cityName);
weatherHelper.addNewCityWeatherData(req.body.cityName);
}
return newAppointment;
})
.then(newAppointment => {
// do some other stuff
res.send(newAppointment);
})
.catch(err => {
error(err);
});
};
Function to add weather data:
exports.addNewCityWeatherData = (city) => {
console.log('City in addNewCityWeatherData', city);
getCurrentTrackingCities(cities => {
if(cities.indexOf(city) < 0) {
console.log(city + ' data not in weather');
getWeatherData(city, data => {
console.log('Got weather data');
addWeatherDataToDB(city, data);
});
} else {
console.log('City exists');
}
});
};
Function to get weather data from API:
getWeatherData = (city, callback) => {
console.log('getWeatherData called', city);
let url = `http://api.apixu.com/v1/forecast.json?key=${weatherApiKey}&q=${city}&days=${10}`
request(url, (err, res, body) => {
console.log('Weather data received body');
callback(body);
});
};
When testing, this feature fails and all console logs are printed except the 'Weather data received body' and the logs in consequent functions.
Here is my test:
describe.only('Weather data', function() {
let requestWithSession = request.defaults({jar: true});
let hashedPass = bcrypt.hashSync('testpass', null);
beforeEach((done) => {
new User({
'name': 'Test User',
'email': 'testuser#test.com',
'password': hashedPass
})
.save()
.then(() => {
let options = {
'method': 'POST',
'uri': testHost + '/login',
'form': {
'email': 'testuser#test.com',
'password': 'testpass'
}
};
requestWithSession(options, (err, res, body) => {
done();
});
});
}); // beforeEach
afterEach((done) => {
// remove test stuff from db
}); // afterEach
it('Adds weather data when an appointment with new city is posted', (done) => {
let options = {
'method': 'POST',
'uri': testHost + '/schedule',
'form': {
'title': 'Test title',
'description': 'Test description',
'start_date_time': '2017-07-19 01:00',
'end_date_time': '2017-07-19 02:00',
'cityName': 'New York',
'isTrackingWeather': 'true'
}
};
// post request to add appointment data
requestWithSession(options, (err, res, body) => {
if(err) {
console.log('DatabaseError in Weather Data');
throw {
type: 'DatabaseError',
message: 'Failed to create test setup data'
};
}
let options = {
'method': 'GET',
'uri': testHost + '/allweather'
};
// subsequesnt request to get updated weather data
requestWithSession(options, (err, res, body) => {
let found = false;
weatherData = JSON.parse(body);
// console.log('weatherData in test', weatherData);
weatherData.forEach(weather => {
if(weather.location && weather.location.name === 'New York') {
found = true;
}
});
expect(found).to.be.true;
done();
});
});
});
}); // Weather Data
Here is the terminal output:
Can anyone please tell me what am I doing wrong?
When you run your test is that the test suite make a request to your test server, and the code that handles the request in your test server makes another request to another host.
You do not get to see 'Weather data received body' because the request handled by your test server is not waiting for the request that the test server itself makes. addNewCityWeatherData has no callback and does not return a promise, so the code that calls it goes on its merry way without waiting for it to complete. You should modify it to allow for the calling code to wait for a result.
Also, I'm not seeing how the data from the request initiated by your test server is folded back into the request that comes from your test suite. You may have to add some code for that too, unless addWeatherDataToDB(city, data); is taking care of it automatically somehow.
Related
Meteor newbie here, I want to do a meteor call to a meteor method which does a get request to an 3rd party API and forwards the returned JSON response to my initial meteor call.
I want to use the returned JSON response to render my html code
here is my js file with meteor method
Meteor.methods({
'jira.get'(projectName, maxResult) {
if (!this.userId) {
throw new Meteor.Error('Not authorized.');
}
check(projectName, String);
check(maxResult, String);
const base64Auth = Meteor.users.findOne({ _id: this.userId }).Authorization;
const options = {
method: 'GET',
url: `https://myCompany.com/.../search?jql=project=${projectName}&maxResults=${maxResult}`,
headers: {
Authorization: base64Auth,
},
};
const future = new Future();
request(options, function(error, response) {
if (error) throw new Error(error);
const jiraResponse = JSON.parse(response.body);
future.return(jiraResponse);
});
return future.wait();
},
});
and my JSX file that calls above Meteor method is as below
export const App = () => {
Meteor.call('jira.get', project='test', maxResult = '10', (error, jiraResponse) => {
console.log("this is working fine: "jiraResponse)
});
console.log('this is undefined', jiraResponse)
}
If i use useState as below, initially the last console log console.log(jiraResp) prints {} as expected but it goes into infinite loop with the correct data after that
const [ jiraResp, setjiraResp ] = useState({})
Meteor.call('jira.get', project='test', maxResult = '10', (error, jiraResponse) => {
if (jiraResponse){
console.log("got the resp ")
setjiraResp(jiraResponse);
}
else{
console.log("not recieved")
}
});
console.log(jiraResp)
How do i get the response of meteor call and update my jiraResponse just once ?
Setting jiraResp in the method callback will trigger a re-render and since you are making the method call in the render method itself, it will repeat the call, hence the loop.
You need to use useEffect:
const [ jiraResp, setjiraResp ] = useState({});
useEffect(() =>
Meteor.call('jira.get', project = 'test', maxResult = '10', (error, jiraResponse) => {
if (jiraResponse) {
console.log("got the resp ");
setjiraResp(jiraResponse);
} else {
console.log("not recieved");
}
}), []);
console.log(jiraResp);
So I have a POST route that calls a function:
router.route('/generateSeed').post(function(req,res){
generate_seed(res)
});
UPDATE: Here is the genrate_seed() function
function generate_seed(res)
{
var new_seed = lightwallet.keystore.generateRandomSeed();
generate_addresses(new_seed, res);
}
var totalAddresses = 0;
function generate_addresses(seed, res)
{
if(seed == undefined)
{
console.log("seed")
}
var password = Math.random().toString();
lightwallet.keystore.createVault({
password: password,
seedPhrase: seed,
hdPathString: "m/44'/60'/0'/0" //added this changing from Default m/0'/0'/0'/
}, function (err, ks) {
ks.keyFromPassword(password, function (err, pwDerivedKey) {
if(err)
{
}
else
{
ks.generateNewAddress(pwDerivedKey, totalAddresses);
var addresses = ks.getAddresses()
var web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io"));//changed to infura as a provider
var html = "";
var address = addresses;
var seedPhrase = seed;
addToAPI(address,seedPhrase, res); //address
}
});
});
}
function addToAPI(address, seedPhrase, res){
var NewUser = {
publicK: address,
seed: seedPhrase
}
axios.post('http://localhost:3000/CryptoWallet/add/createCryptoWallet', NewUser)//changed from localhost
.then((res)=>{
console.log("Response");
})
.catch(error=>{
console.log(error);
})
}
Which calls to this second route:
router.route('/add/createCryptoWallet').post(function(req,res){
var crypto_wallet = new CryptoWallet(req.body)
console.log("The cyrptoWallet on create", crypto_wallet);
crypto_wallet.save()
.then(crypto_wallet =>{
res.json({data: CryptoWallet({_id:1})}); //<<--- I want this line
})
.catch(err => {
res.status(400).send("unable to save CryptoWallet to databse");
});
});
UPDATE I do get it to POST and save in the database. Right now I can only get the response from the first POST route is there a way to get the response from the second POST route my final goal is the get the _id created by mongo as a response.
Thanks ahead!
You are missing response sending for your first POST request (/generateSeed). addToAPI function need to wait until second POST request is finished and the send its own response. So basically it should look similar to this:
function addToAPI(address, seedPhrase, res){
var NewUser = {
publicK: address,
seed: seedPhrase
}
axios.post('http://localhost:3000/CryptoWallet/add/createCryptoWallet', NewUser)
.then((response)=>{
res.json(response.data); // axios wrappes the body of response into `data` object
})
.catch(error=>{
console.log(error);
res.status(500).('Some error occured');
})
}
I have a problem regarding push notifications. I have this error in firebase functions logs:
Error: To send a message with a payload, the subscription must have
'auth' and 'p256dh' keys.
exports.storePostData = functions.https.onRequest(
(request, response) => {
cors(request, response, () => {
admin.database().ref('posts').push({
id: request.body.id,
title: request.body.title,
location: request.body.location,
image: request.body.image
}).then(() => {
webpush.setVapidDetails('mailto: xxxx#gmail.com', 'BLl7xIPAyJNzsMi5vo_aG-4RdXdyZ4Q4ZFpTgnm902qN79MIiSORBk9N-rfFEGiKNPuJu5SJmUX35Wwce9nuH94', 'M8E6hw7jCmu7qNQJ88FV5o02OAiLefEFJK8jyJimk7g')
return admin.database().ref('subscriptions').once('value');
}).then(subscriptions => {
subscriptions.forEach(sub => {
var pushConfig = {
endpoint: sub.val().endpoint,
keys: {
auth: sub.val().keys,
p256dh: sub.val().p256dh
}
}
webpush.sendNotification(pushConfig, JSON.stringify({
title: 'New Post',
content: 'New post added',
openUrl: '/help'
})).catch(err => {
console.log(err)
})
})
response.status(201).json({
message: 'Data stored',
id: request.body.id
})
}).catch(err => {
response.status(500).json({
error: err
})
})
})
});
This is my function for storing post data, and i think the problem is here because it can't even get to push event in serviceWorker (i don't get any logs there).
On the client side, you should be able to retrieve subscription
On Angular it would be like
const sw = await navigator.serviceWorker.register('/assets/js/service-worker.js', { scope: '/assets/js/' });
let subscription = await sw.pushManager.getSubscription();
Through this way, you should be able to get subscription and subscription object contains p256dh and auth keys.
You should be able to see the values by
console.log("subscription: ", subscription?.toJSON());
Ye i figured it need to go as:
keys: {
auth: sub.val().keys.auth,
p256dh: sub.val().keys.p256dh
}
I am making a skill for the Amazon Echo. In my handlers, I have an intent (SelectGardenIntent) that obtains the user_id (needed for following HTTP requests) from the access token successfully, as well as a variable called gardenNumber which is a slot value. To complete the request, I need two other values, the garden_id and the dev_id. I use this gardenNumber and pass it into a function called getGardenId, which will assign the one of the data from the HTTP request to the variable garden_id I have defined in index.js. There are no issues with user_id and gardenNumber. When the function is run, there are no errors from the request, but the callback function with the response is also not executed. The user_id, "about to enter request", and "req done" are correctly logged when tested, but the other log statements in the callback function are not since it is not run. The result is that garden_id is undefined. dev_id is obtained in another method that depends on this garden_id, so dev_id is also undefined. Please help me on this issue. I have pasted the relevant code below.
...
var user_id, garden_id, dev_id;
...
function getGardenId (gardenNumber) {
console.log(user_id);
var path = '/api/rest/client/getgardeninfo?&userid=' + user_id;
var options = {
hostname: server_ip,
port: 80,
path: path,
method: 'GET'
}
console.log("about to enter request");
var req = http.request(options, (res) => {
console.log('entered request');
if (res.statusCode === 200) {
console.log('successful request');
res.setEncoding('utf8');
var body = "";
res.on('data', (chunk) => {
console.log('adding data');
body += chunk.toString();
});
res.on('end', () => {
var obj = JSON.parse(body);
console.log('successfully parsed');
if (obj.error === 200) {
console.log('##gardenid successfully obtained');
garden_id = obj.data[gardenNumber - 1].id;
} else {
console.log("parsing error");
}
});
} else {
console.log("failed request");
}
}); } catch(e) {
console.log("ERROR");
}
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.on('finish', () => {
console.log('ended');
})
req.end();
console.log("req done");
}
...
var handlers = {
...
'SelectGardenIntent': function () {
//var filledSlots = delegateSlotCollection.call(this);
var gardenNumber = this.event.request.intent.slots.Garden.value;
user_id = this.event.session.user.accessToken;
getGardenId(gardenNumber);
getDevId(garden_id);
this.emit(':tell', `OK, garden ${gardenNumber} selected, user id is ${user_id}, garden id is ${garden_id}, device id is ${dev_id}`);
}
...
}
You'd better use npm request to make calls.
request.get({
url: 'http://' + server_ip + '/api/rest/client/getgardeninfo?&userid=' + user_id
}, function (err, res, body) {
console.log(body);
})
I have connected my Database to auth0 and when I try the connection is returns 401 unauthorized access. I have allowed auth into my firewall and the password is correct. How come it is returning this error when searching for username and password?
More info
in my easy tables I made them authenticated access only, do I have to do something to get around this?
function login(email, password, callback) {
//this example uses the "tedious" library
//more info here: http://pekim.github.io/tedious/index.html
var Connection = require('tedious#1.11.0').Connection;
var Request = require('tedious#1.11.0').Request;
var TYPES = require('tedious#1.11.0').TYPES;
var connection = new Connection({
userName: 'username',
password: 'pass',
server: 'server',
options: {
database: 'db',
encrypt: true,
rowCollectionOnRequestCompletion: true
}
});
var query = "SELECT Id, Email, Password " +
"FROM user WHERE Email = #Email";
connection.on('debug', function (text) {
// Uncomment next line in order to enable debugging messages
// console.log(text);
}).on('errorMessage', function (text) {
console.log(JSON.stringify(text, null, 2));
return callback(text);
}).on('infoMessage', function (text) {
// Uncomment next line in order to enable information messages
// console.log(JSON.stringify(text, null, 2));
});
connection.on('connect', function (err) {
if (err) { return callback(err); }
var request = new Request(query, function (err, rowCount, rows) {
if (err) {
callback(new Error(err));
} else if (rowCount < 1) {
callback(new WrongUsernameOrPasswordError(email));
} else {
bcrypt.compare(password, rows[0][2].value, function (err, isValid) {
if (err) { callback(new Error(err)); }
else if (!isValid) { callback(new WrongUsernameOrPasswordError(email)); }
else {
callback(null, {
user_id: rows[0][0].value,
email: rows[0][1].value
});
}
});
}
});
request.addParameter('Email', TYPES.VarChar, email);
connection.execSql(request);
});
}
Since you are using Azure Mobile App, which included the Node.js server SDK for your app. Then you don't need to install tedious to work with Azure SQL database. The SDK has already wrapped up mssql to do this. So basically you can use this code sample to connect your database.
var api = {
// an example of executing a SQL statement directly
get: (request, response, next) => {
var query = {
sql: 'UPDATE TodoItem SET complete = #completed',
parameters: [
{ name: 'completed', value: request.query.completed }
]
};
request.azureMobile.data.execute(query)
.then(function (results) {
response.json(results);
});
},
// an example of executing a stored procedure
post: (request, response, next) => {
var query = {
sql: 'EXEC completeAllStoredProcedure #completed',
parameters: [
{ name: 'completed', value: request.query.completed }
]
};
request.azureMobile.data.execute(query)
.then(function (results) {
response.json(results);
});
}
};
module.exports = api;
For more information, please refer to this documentation.