I want to implement error handling in my app but when I throw a Meteor.Error my server crashes. This might be because I'm using a future to wait for the result. How can I get this running?
Meteor.methods({
'/app/pdf/download': function (url, name) {
check(url, String);
check(name, Match.Any);
if ( ! name) {
name = url.split('/').pop();
} else {
name += '.pdf';
}
var Future = Meteor.npmRequire('fibers/future');
var Download = Meteor.npmRequire('download');
var future = new Future();
var download = new Download({ extract: true, strip: 1 })
.get(url)
.dest(process.env.PWD + '/staticFiles')
.rename(name);
// Run download
download.run(function (err, files, stream) {
if (err) {
throw new Meteor.Error(500, 'Couldn\'t download file');
}
future.return(name);
});
return future.wait();
}
});
Yes, it is because you are throwing it in another call stack.
You could try:
var error;
download.run(function (err, files, stream) {
if (err) {
error = err;
}
future.return(name);
});
var result = future.wait();
if (error)
throw new Meteor.Error(500, 'Couldn\'t download file');
return result;
Either way I recommend using Meteor.wrapAsync for your purpose.
var sync = Meteor.wrapAsync(function (done) {
download.run(function (err, files, stream) {
if (err) {
done(new Meteor.Error(500, 'Couldn\'t download file'));
}
else {
done(err, name);
}
});
};
return sync();
If you are using Meteor < 1.0, its Meteor._wrapAsync().
Related
I have an existing async function:
async doJSONGetRequest(getUrl, accessToken) {
return new Promise(function(resolve, reject) {
const reqHeaders = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
};
console.info('url = ' + getUrl);
request.get({
url: getUrl,
headers: reqHeaders,
}, function(err, response) {
if (err) return reject(err);
try {
// console.debug(`response = ${response.body}`);
const parsed = JSON.parse(response.body);
return resolve(parsed);
} catch (err) {
return reject(err);
}
});
});
}
}
I'm trying to test it with Jasmine(v4).
Of course, I don't want this thing to actually make an HTTP request, so I tried rigging up a spy on the 'request' package's 'get' function in the 'beforeAll' section:
describe('RAPIDAPIService', function() {
beforeAll(async function() {
spyOn(request, 'get')
.and
.callFake(async (parameters) => {
if (parameters.url === 'http://localhost/api/getSomething') {
const rsp = {};
rsp.body = 'good stuff';
return rsp;
} else if (parameters.url === 'http://localhost/api/whoops') {
return new Error('401 not found');
} else {
return null;
}
});
});
it('doJSONGetRequest should run successfully', async () => {
expect(api.doJSONGetRequest).toBeDefined();
const res = await api.doJSONGetRequest('http://localhost/api/getSomething', '12345678');
expect(data).toEqual('good stuff');
});
it('doJSONGetRequest should resolve errors properly', async () => {
expect(api.doJSONGetRequest).toBeDefined();
const res = await api.doJSONGetRequest('http://localhost/api/whoops', '12345678');
const expectedError = new Error('401 not found');
expect(res).toEqual(expectedError);
});
Console log statements seem to indicate that I'm actually getting past / returning something from my "await" calls in the "it" tests. But the spies are actually working / detecting that the url's have been called.
(Note that I'm not including here other tests in the same file that do not make asynchronous calls and ARE working... just so you know that there's no problem accessing the actual "api" library and its functions.)
These two tests keep failing with "Error: Timeout - Async function did not complete within 5000ms". And like I said, it seems like they're not returning back to the tests from their calls to the doJSONGetRequest function.
Any thoughts?
Thanks!
I am thinking the issue is the mocking. request.get seems to take two parameters and I am thinking you need to call the 2nd parameter (callback function) once you are done so the resolve can be called.
Try this:
spyOn(request, 'get')
.and
// add callbackFunction as 2nd argument
.callFake((parameters, callbackFunction) => {
if (parameters.url === 'http://localhost/api/getSomething') {
const rsp = {};
rsp.body = 'good stuff';
callbackFunction(null, rsp);
} else if (parameters.url === 'http://localhost/api/whoops') {
callbackFunction({ error: '401 not found' }, {});
} else {
callbackFunction(null, null);
}
});
I am using the following code to get the list of documents using pagination. The code is working fine. But how do I find the continuation token if I want to send it from the client for pagination.
function queryCollectionPaging() {
return new Promise((resolve, reject) => {
function executeNextWithRetry(iterator, callback) {
iterator.executeNext(function (err, results, responseHeaders) {
if (err) {
return callback(err, null);
}
else {
documents = documents.concat(results);
if (iterator.hasMoreResults()) {
executeNextWithRetry(iterator, callback);
}
else {
callback();
}
}
});
}
let options = {
maxItemCount: 1,
enableCrossPartitionQuery: true
};
let documents = []
let iterator = client.queryDocuments( collectionUrl, 'SELECT r.partitionkey, r.documentid, r._ts FROM root r WHERE r.partitionkey in ("user1", "user2") ORDER BY r._ts', options);
executeNextWithRetry(iterator, function (err, result) {
if (err) {
reject(err)
}
else {
console.log(documents);
resolve(documents)
}
});
});
};
You could find the continuation token in the responseHeaders parameter, please try to use responseHeaders ['x-ms-continuation'] to grab it.
Such as :
continuationToken = responseHeaders ['x-ms-continuation'];
Then you could pass the token as a parameter to the execute method.
let options = {
maxItemCount: 1,
enableCrossPartitionQuery: true,
continuation : continuationToken
};
If the continuationToken is null, it means no more results.
You could refer to my previous case: How to get & set Cosmos Db continuation token in javascript.
We are trying to connect our evothings app up to a meteor server.
To do this we are using a lib called asteroid. However we are unable to connect and run methods. We are absolutly sure this is not a server issue since we have some separate client code for testing that works flawlessly with it.
Evothings says it should work with websockets, and we aren't getting any error output, but all our method calls are returning nothing.
Here is the code:
var _asteroid = require('asteroid');
var Asteroid = (0, _asteroid.createClass)('password-login');
var asteroid = new Asteroid({ endpoint: 'wss://[url]/websocket' });
var currentLogin = null;
$('#login').submit(function(event) {
event.preventDefault();
login($('#login_username').val(), $('#login_password').val());
});
$('#create').submit(function(event) {
event.preventDefault();
newUser($('#create_username').val(), $('#create_password').val(), $('#create_id').val());
});
$('#occupy').click(function(event) {
setStatus(0);
});
$('#vacant').click(function(event) {
setStatus(1);
});
$('#refreash').click(function() {
getEmptyRooms();
});
window.newUser = function (username, password, roomId) {
$('#create_error').text('');
asteroid.call("accounts.newUser", username, password, roomId).then(function (result) {
console.log("Success");
login(username, password);
}).catch(function (error) {
console.log("Error");
console.error(error);
$('#create_error').text(error.message);
});
}
window.login = function (username, password) {
$('#login_error').text('');
asteroid.loginWithPassword({ username: username, password: password }).then(function (result) {
console.log(result);
currentLogin = result;
$('#current').html('Current User: ' + username);
}).catch(function (error) {
console.log("Error");
console.error(error);
$('#login_error').text(error.message);
});;
}
window.getEmptyRooms = function () {
asteroid.call("rooms.getAvailable").then(function (result) {
console.log(result);
$('#room_list').empty();
for(i = 0; i < result.length; i++) {
$('#room_list').append('<li>' + result[i] + '</li>');
}
}).catch(function (error) {
console.log("Error");
console.error(error);
});
}
window.setStatus = function (status) {
$('#status_error').text('');
if (currentLogin != null) {
asteroid.call("rooms.setStatus", status).then(function (result) {
console.log(result);
}).catch(function (error) {
console.log("Error");
console.error(error);
$('#status_error').text(error.message);
});
} else {
console.log('please login first');
$('#status_error').text('please login first');
}
}
As far as I know, the require() function works only in node.js, not in browser environment such as Evothings Viewer or Cordova, so you'll need some alternative means of loading the "asteroid" lib. Browserify?
How did you look for error output? The Evothings Tools window? If so, did you add this snippet to your index.html file?
<script>
// Redirect console.log to Evothings Workbench.
if (window.hyper && window.hyper.log) { console.log = hyper.log }
</script>
Perhaps this error isn't exclusive to the Evothings environment. Have you tested the app in a regular web browser?
Are you using proper certs?
Self signed will not work. The Evothings app is served via wss and since it runs "headless" so to speak (not a normal browser) it can't ask the user about allowing a self signed cert, so it will fail. Note that AFAIK ANY issue with the cert will make it fail.
when i try to run nodejs, there is an error in "throw er;" in the "css" part, but it doesn't exist. Someone can see the error?
var http = require('http'),
fs = require('fs');
http.createServer(function(req, res) {
fs.readFile('./index.html', function (err, data) {
if (err) { throw err; }
res.writeHeader(200, {'Content-Type': 'text/html'});
res.write(data);
res.end();
});
fs.readFile('./main.js', function (err, data) {
if (err) { throw err; }
res.writeHead(200, {'Content-Type': 'text/javascript'});
res.end(data);
res.end();
});
fs.readFile('./style.css', function (err, data) {
if (err) { throw err; }
res.writeHead(200, {'Content-Type': 'text/css'});
res.write(data);
res.end();
});
}).listen(8000);
you have a few problems in your code:
at the main.js part you use res.end instead of res.write
you call res.end 3 times
you try to return 3 files in one request, you should check the path (req.url) and return the right file by that. or you can use express or other framework.
I am trying to learn Meteor, starting by writing a simple application where the server calls an HTTP API based on user input, processes the information, then returns it to the client to display it.
I am not having much success. I can't seem to return the result from server to client:
if (Meteor.isServer) {
Meteor.methods({
checkTransit: function(method, url, options) {
this.unblock();
return Meteor.http.call(method, url, function(error, result) {
if (error) {
console.log('SERVER ERRR');
console.log(error);
} else {
console.log('SERVER RESULT');
console.log(result);
}
});
}
})
}
if (Meteor.isClient) {
Template.body.events({
"submit .new-task": function(event) {
// Prevent default browser form submit
event.preventDefault();
var text = event.target.text.value;
var method = 'GET';
var url = 'http://sometransitapi.com';
var options = {
headers: {
'accept': 'application/XML',
'content-type': 'application/XML'
}
}
Meteor.call('checkTransit', method, url, options, function (error, result) {
if (error) {
console.log('CLIENT ERRR');
console.log(error);
} else {
console.log('CLIENT RESULT');
var parser;
parser = new DOMParser();
var xmlDoc
xmlDoc = parser.parseFromString(result, "text/xml");
console.log(xmlDoc);
}
})
}
})
}
I can see the results being returned to the result variable at isServer, but I can not pass the result to the xmlDoc variable in isClient. What am I doing wrong? Is this the correct way to structure things for what I want to do in Meteor?
Meteor.http.call is being called asynchronously in your server code (you are passing a callback). The functions in your Meteor.methods object expect to return a value, so you should be calling Meteor.http.call synchronously. Changing your server code to below should do the trick.
if (Meteor.isServer) {
Meteor.methods({
checkTransit: function(method, url, options) {
this.unblock();
// This will throw an exception and return it as the error object
// in your Meteor.call if an error occurs, otherwise it will
// return an empty error object and the result object will be
// the return value from Meteor.http.call
return Meteor.http.call(method, url);
}
})
}
If you want to keep the logic on the server side then #Curtis' answer should help you.
Looking at what you have, I don't see much of a point of getting the server to do the work. The Http api is available everywhere in Meteor. You are just fetching data, which can be solely done on the front end. This would also technically make it faster. Taking your code and moving it around a bit you get the following.
if (Meteor.isClient) {
Template.body.events({
"submit .new-task": function(event) {
// Prevent default browser form submit
event.preventDefault();
var text = event.target.text.value;
var method = 'GET';
var url = 'http://sometransitapi.com';
var options = {
headers: {
'accept': 'application/XML',
'content-type': 'application/XML'
}
}
Http.call(method, url, options, function (error, result) {
if (error) {
console.log('CLIENT ERRR');
console.log(error);
} else {
console.log('CLIENT RESULT');
var parser;
parser = new DOMParser();
var xmlDoc
xmlDoc = parser.parseFromString(result, "text/xml");
console.log(xmlDoc);
}
});
}
});
}