Iron Router throwing exception in onData - meteor

I have seen a strange exception in the onData() function of a Route Controller in iron-router. The exception only seems to occur after I make a change to a file which of course causes meteor to restart the app. I've put up a sample in github that demonstrates the problem here: https://github.com/benmonro/iron-router-bug
If you add a person by clicking submit, then click on that person's name, everything will load fine. However, if you then make a change to some js (say add a comment). Meteor will reload and you'll get the exception in the 'onData()' method which is attempting to use a property of the data returned from the 'data()' function.
The exception is here:
> Exception in defer callback: TypeError: Cannot read property 'name' of
> undefined
> at RouteController.extend.onData (http://localhost:3000/irDataBug.js?8327f0bf1bedee2437ec14bc4509d8f7e85079ab:33:19)
> at RouteController.runHooks (http://localhost:3000/packages/iron-router.js?e9fac8016598ea034d4f30de5f0d356a9a24b6c5:827:16)
> at http://localhost:3000/packages/iron-router.js?e9fac8016598ea034d4f30de5f0d356a9a24b6c5:2137:16
> at Utils.extend._run.withNoStopsAllowed (http://localhost:3000/packages/iron-router.js?e9fac8016598ea034d4f30de5f0d356a9a24b6c5:2098:21)
> at _assign._compute (http://localhost:3000/packages/deps.js?4a82362ae66e863a1c1a8b0a5fec6f665e2038d1:228:38)
> at new Deps.Computation (http://localhost:3000/packages/deps.js?4a82362ae66e863a1c1a8b0a5fec6f665e2038d1:160:10)
> at Object._assign.autorun (http://localhost:3000/packages/deps.js?4a82362ae66e863a1c1a8b0a5fec6f665e2038d1:380:13)
> at http://localhost:3000/packages/iron-router.js?e9fac8016598ea034d4f30de5f0d356a9a24b6c5:2134:12
> at Utils.extend._run.withNoStopsAllowed (http://localhost:3000/packages/iron-router.js?e9fac8016598ea034d4f30de5f0d356a9a24b6c5:2098:21)
> at _assign._compute (http://localhost:3000/packages/deps.js?4a82362ae66e863a1c1a8b0a5fec6f665e2038d1:228:38)

The data isn't ready after the reload so you need to add a guard or an early return to the callback. Here's a simple fix:
onData: function() {
var thePerson = Router.current().data();
if (!thePerson)
return;
if (thePerson.name === "Fred") {
console.log("it's fred");
} else {
console.log("it's not fred");
}
}
Because the callback is reactive, it will rerun once thePerson is found. Alternative solutions could involve a waitOn callback and then checking for this.ready().

Related

Meteor publication send custom sanitized error to client in publication

I am not sure if I am just doing something wrong or if this is actually not working. I want to display the original publication error on the client, in case I catched one:
Meteor.publish('somePub', function (args) {
const self = this
try {
// ... publication logic
catch (pubErr) {
self.error(pubErr)
}
})
On the client I am "catching" this error via the onStop callback:
Meteor.subscribe('somePub', args, {
onStop: function (e) {
// display e to user
}
})
However, while on the server the pubErr is a Meteor.Error and according to the documentation it should be sent to the client, the client just receives a generic sanitized error message:
on the server
{
stack: "useful stack of actual method calls",
error: "somePub.failed",
reason: "somePub.invalidArguments",
details: { arg: undefined }
}
on the client
{
stack: "long list of ddp-message calls",
isClientSafe: true,
error: 500,
reason: "Internal server error",
details: undefined,
message: "Internal server error [500]",
errorType: "Meteor.Error"
}
Note: I also tried to add the error to itself as sanitizedError field, as in the documentation mentioned, but no success either.
Am I missing something here?
Actually I found the answer to the issue after being pointed into the right direction.
The example code works fine on a new project, so I checked why not in my project and I found, that I did not surround the arguments validation using SimpleSchema by the try/catch (unfortunately my question was poorly designed as it missed this important fact, mainly because I abstracted away the schema validation from the publication creation):
Meteor.publish('somePub', function (args) {
pubSchema.validate(args) // throws on fail
const self = this
try {
// ... publication logic
catch (pubErr) {
self.error(pubErr)
}
})
So I thought this could not be the issue's source but here is the thing: Simple Schema is not a pure Meteor package but a NPM package and won't throw a Meteor.Error but a custom instance of Error, that actually has the same attributes (error, errorType, details) like a Meteor.Error, see this part of the source code of a validation context.
So in order to pass the correct information of a SimpleSchema validation error to the client you should
wrap it in a try/catch
add the isClientSafe flag to it
alternatively convert it to a Meteor.Error
Attach a custom Meteor.Error as sanitizedError property to it

How to access custom response values from a page script?

This might sound like a silly question but I have really tried everything I could to figure it out. I am creating a variable and adding it to my response object in custom Express server file like so:
server.get('*', (req, res) => {
res.locals.user = req.user || null;
handle(req, res);
});
Now I want to access this res.locals.user object from all of my pages, i.e. index.js, about.js, etc., in order to keep a tab on the active session's user credentials. It's got to be possible some way, right?
P.S.: Reading some thread on the NextJS Github page, I tried accessing it from my props object as this.props.user but it keeps returning null even when a server-side console.log shows non-null values.
The res object is available on the server as a parameter to getInitialProps. So, with the server code you have above, you can do
static async getInitialProps({res}) {
return { user: res.locals.user }
}
to make it available as this.props.user.

Meteor [Error: Can't wait without a fiber] after a call to Email.send

I've created a very simple server using Meteor, to send an email after a timeout. When I use a timeout, the message is successfully sent but an error is thrown: [Error: Can't wait without a fiber].
Here's my code:
if (Meteor.isServer) {
Meteor.startup(function () {
// <DUMMY VALUES: PLEASE CHANGE>
process.env.MAIL_URL = 'smtp://me%40example.com:PASSWORD#smtp.example.com:25';
var to = 'you#example.com'
var from = 'me#example.com'
// </DUMMY>
//
var subject = 'Message'
var message = "Hello Meteor"
var eta_ms = 10000
var timeout = setTimeout(sendMail, eta_ms);
console.log(eta_ms)
function sendMail() {
console.log("Sending...")
try {
Email.send({
to: to,
from: from,
subject: subject,
text: message
})
} catch (error) {
console.log("Email.send error:", error)
}
}
})
}
I understand that I could use Meteor.wrapAsync to create a fiber. But wrapAsync expects there to be a callback to call, and Email.send doesn't use a callback.
What should I do to get rid of the error?
This happens because while your Meteor.startup function runs inside a Fiber (like almost all other Meteor callbacks), the setTimeout you use does not! Due to the nature of setTimeout it will run on the top scope, outside the fiber in which you defined and/or called the function.
To solve, you could use something like Meteor.bindEnvironment:
setTimeout(Meteor.bindEnvironment(sendMail), eta_ms);
And then do so for every single call to setTimeout, which is a painfully hard fact.
Good thing it's not actually true. Simply use Meteor.setTimeout instead of the native one:
Meteor.setTimeout(sendMail, eta_ms);
From the docs:
These functions work just like their native JavaScript equivalents. If you call the native function, you'll get an error stating that Meteor code must always run within a Fiber, and advising to use Meteor.bindEnvironment
Meteor timers just bindEnvironment then delay the call as you wanted.

Meteor: The application is not spiderable

My application is not spiderable both on local and production.
When I go to http://localhost:3000/?_escaped_fragment_=, I can see the following error appears (phantom is killed after 15 seconds):
spiderable: phantomjs failed: { [Error: Command failed: ] killed: true, code: null, signal: 'SIGTERM' }
It seems that many other people got this problem:
https://github.com/gadicc/meteor-phantomjs/issues/1
https://groups.google.com/forum/#!msg/meteor-talk/Lnm9HFs4MgM/YKDMR80fVecJ
https://groups.google.com/forum/#!topic/meteor-talk/7ZbidddRGo4
The thing is I am not using observatory or select2 and all my publications return a cursor. According to me, the problem comes from the minification. I just read in this thread that someone succeed to display "SyntaxError: Parse error". How can I know more about what is going wrong with Phantom and which file is causing the problem?
This happens when spiderable is waiting for subscriptions that fail to return any data and end up timing out, as mentioned in some of the threads you linked.
Make sure that all of your publish functions are either returning a cursor, a (possibly empty) list of cursors, or sending this.ready().
Meteor APM may be useful in determining which publications aren't returning.
If you want to know more about what is wrong with phatomjs, you might try this code (1):
// Put your URL below, no "?_escaped_fragment_=" necessary
var url = "http://your-url.com/";
var page = require('webpage').create();
page.open(url);
setInterval(function() {
var ready = page.evaluate(function () {
if (typeof Meteor !== 'undefined'
&& typeof(Meteor.status) !== 'undefined'
&& Meteor.status().connected) {
Deps.flush();
return DDP._allSubscriptionsReady();
}
return false;
});
if (ready) {
var out = page.content;
out = out.replace(/<script[^>]+>(.|\n|\r)*?<\/script\s*>/ig, '');
out = out.replace('<meta name=\"fragment\" content=\"!\">', '');
console.log(out);
phantom.exit();
}
}, 100);
For use in local, install phantomjs. Then outside your app, create a file phantomtest.js with the code above. And run phantomjs phantomtest.js
Another thing that maybe you can try is to use UglifyJS to catch some errors in the minified JS file as Payner35 did.
My problem was coming from SSL. You can have a complete overview of what I did here.
Edit the spiderable source and add --ignore-ssl-errors=yes to the phantomjs command line, it will work.

Meteor Autorun() not running when accessing collection.property

Something strange is going on; when I run the following code, it works:
Deps.autorun(function() {
var room = Rooms.findOne({'room_id':Session.get('room_id')});
// var p = room.room_id;
console.log('autorun');
}
However, if I uncomment the var p line, it (the whole block) stops running. What's happening?
Found this in the depths of the Meteor.js documentation: "If the initial run of an autorun throws an exception, the computation is automatically stopped and won't be rerun."
On the first run of autorun, which is when the page is loaded, the database hasn't been loaded which throws an exception when you try to access room.room_id, and the autorun immediately stops from running again. Fixed by adding:
if (room) {
console.log(room.room_id);
...
}

Resources