synced-cron to run once - Meteor - meteor

I want to run a 'scheduled job' in meteor which needs to be run only once at a specified time. I have the access of the date object. i've tried the below cron expression but didn't get the expected behavior.
here's the code snippet.
schedule: function(parser) {
var _year = bidStartTime.getFullYear();
var _month = bidStartTime.getMonth();
var _date = bidStartTime.getDate();
var _hours = bidStartTime.getHours();
var _min = bidStartTime.getMinutes();
var bidAsCron = _min+' '+_hours+' '+_date+' '+ _month+' ? '+_year;
console.log('parsed as ' + bidAsCron);
// parser is a later.parse object
// sample parser.cron('25 17 5 10 ? 2015');
// should 5th October 2015 at 5:25 pm
return parser.cron(bidAsCron);
},

Using synced-cron version 1.3.0 you can specify parser.recur().on(date).fullDate(); to schedule a once off (i.e not recurring) event like this:
SyncedCron.add({
name: cron_name,
schedule: function (parser) {
// ending_at is a Date object set to some future date
// there is no recurrence
return parser.recur().on(ending_at).fullDate();
},
job: function () {
// job code
}
});
working example: http://meteorpad.com/pad/mLfyoLnHSECPhQscz/synced-cron%20to%20run%20once

Related

Writing logs with cloud logging

I'm having problems with using google-cloud/logging. My objective is to write to a file which is to be created weekly, and i have previously managed to do that. However since yesterday I kept getting this error:
Error: 3 INVALID_ARGUMENT: A monitored resource must be specified for each log entry.
So I updated the google-cloud/logging to the latest version(5.2.2) after reading up about a similar issue of monitored resource not being set automatically. Which did take care of that error, however the logs are not showing up in logs viewer after that change.
My code for the logger utility is as follows
const { Logging } = require('#google-cloud/logging');
exports.LoggingUtil = class LoggingUtil {
constructor(context){
var LogMetadata = {
severity: "INFO",
type: "gce_instance",
labels: {
function_name: process.env.FUNCTION_NAME,
project: process.env.GCLOUD_PROJECT,
region: process.env.FUNCTION_REGION
}
}
this.metadata = {
LogMetadata,
labels: {
execution_id: `${context.eventId}`
}
}
const logging = new Logging();
this.log = logging.log(this.getWeekStamp());
}
getWeekStamp(){
const environmentName = constants.environment.name;
var d = new Date();
var day = d.getDay(),
diff = d.getDate() - day + (day == 0 ? -6:1);
date = new Date(d.setDate(diff)).toLocaleDateString('en-US', { day: '2-digit', month: 'short', year: 'numeric'});
date = date.replace(" ", "-");
return `${date.replace(", ","-")}-week-${environmentName}`;
}
write(text){
var entry = this.log.entry(this.metadata, text);
this.log.write(entry);
}
}
What have I done wrong with this? Any help is appreciated 🙏
I think that you error is related with the way to obtain the metadata variable, because is creating an malformed object that is not readable by the log viewer.
In your method constructor you are creating a metadata object similar to this:
{ "LogMetadata":{
"severity":"INFO",
"type":"gce_instance",
"labels":{
"function_name":process.env.FUNCTION_NAME,
"project":process.env.GCLOUD_PROJECT,
"region":process.env.FUNCTION_REGION
}
},
"labels":{
"execution_id":`${context.eventId}`
}}
that is not a valid MonitoredResource, you can change your code in order to create a valid MonitoredResource for example
var LogMetadata = {
severity: "INFO",
type: "gce_instance",
labels: {
function_name: process.env.FUNCTION_NAME,
project: process.env.GCLOUD_PROJECT,
region: process.env.FUNCTION_REGION
}
}
this.metadata = LogMetadata
this.metadata.labels["execution_id"] = `${context.eventId}`
Example result object
{"severity":"INFO",
"type":"gce_instance",
"labels":{"function_name":process.env.FUNCTION_NAME,
"project":process.env.GCLOUD_PROJECT,
"region": process.env.FUNCTION_REGION,
"execution_id":`${context.eventId}`}
}
Additionally, you can check this example file as a reference to write logs using nodeJS.

the task is getting executed for last value only in grunt custom task

While executing grunt custom task I am iterating a loop, inside the loop I am calling a grunt task while calling I set values using current loop value to the task but when loop execute for alliteration it is setting last value of array always.
var productionUrl = "http://www.abc.de";
var phantomPagesPath = {
"index": "/index.php",
"search": "/Suche?searchString=test",
"warenkorb": "/warenkorb",
"product": "/4-ecksofa",
"cms": "/content/25-service",
"category": "/bestellung",
"pageNotFound": "/404"
};
grunt.initConfig({`phantomas: {
prod: {
options: {
options: {
'timeout': 30
},
buildUi: true
}
}
}`});`
grunt.registerTask('fantomas', 'Custome Phantomas task', function () {
var done = this.async();
var pageUrl = '';
var location = '';
for (var page in phantomPagesPath) {
pageUrl = '';
location = '';
if (phantomPagesPath.hasOwnProperty(page)) {
pageUrl = productionUrl + phantomPagesPath[page];
location = './public/phantomas/' + page + "/";
console.log("process started for: " + pageUrl);
console.log("location: " + location);
grunt.config.set('phantomas.options.url', pageUrl);
grunt.config.set('phantomas.options.indexPath', location);
grunt.task.run('phantomas');
}
}
done();
});
Now Output I am getting
process started for: http://www.abc.de/index.php
location: ./public/phantomas/index/
process started for: http://www.abc.de/Suche?searchString=test
location: ./public/phantomas/search/
process started for: http://www.abc.de/warenkorb
location: ./public/phantomas/warenkorb/
process started for: http://www.abc.de/4-ecksofa
location: ./public/phantomas/product/
process started for: http://www.abc.de/content/25-service
location: ./public/phantomas/cms/
process started for: http://www.abc.de/bestellung
location: ./public/phantomas/category/
process started for: http://www.abc.de/404
location: ./public/phantomas/pageNotFound/
Running "phantomas:prod" (phantomas) task
PHANTOMAS EXECUTION(S) STARTED.
Task are getting executed for all number of entries but the data sent for task is the last entry from loop "pageNotFound",
I mean this process is working 7 times but on each process, it is taking last value of loop.
After searching a lot I came up with the solution that inspite of calling
grunt.task.run('phantomas');
Within the foreach loop, it should be called outside the loop which means the task is going to be executed for one time only. So for this, we need to increase make tasks/target within the task for which I did following code:
grunt.loadNpmTasks('grunt-phantomas');
var phantomPagesPath = {
"index": "/index.php",
"search": "/Suche?searchString=test",
"warenkorb": "/warenkorb",
"product": "/4-ecksofa",
"cms": "/content/25-service",
"category": "/bestellung",
"pageNotFound": "/404"
};
grunt.initConfig({
phantomas: {
//For Executing this task run: grunt fantomas
},
});
// Register customer task for phantomas
grunt.registerTask('fantomas', 'Custome Phantomas task', function () {
var done = this.async();
var pageUrl = '';
var location = '';
for (var page in phantomPagesPath) {
pageUrl = productionUrl + phantomPagesPath[page];
location = './public/phantomas/' + page + "/";
grunt.config.set('phantomas.'+page+'.options.url', pageUrl);
grunt.config.set('phantomas.'+page+'.options.indexPath', location);
grunt.config.set('phantomas.'+page+'.options.buildUi', true);
grunt.config.set('phantomas.'+page+'.options.options.timeout', 30);
var pageUrl = '';
var location = '';
}
grunt.task.run('phantomas');
done();
});
Now run
grunt fantomas

Meteor template autorun (Session variable)

Imagine I have a session variable that holds an image source. Every second, I want to the helper that contains this session to run.
if (Meteor.isClient) {
Template.TargetTask.helpers({
'imageSrc': function (e, template) {
var clock = setTimeout(function() {
var position = IMAGE_POOL.pop();
Session.set("currentTarget", position);
}, 1000);
var position = Session.get("currentTarget");
var imageSrc = '/' + position + '.bmp';
return imageSrc;
}
});
the image sources are coming from a global IMAGE_POOL. However, it is possible that the pool contains two same images consecutively. In this case, Session.set() will be called with the same argument and the session will remain unchanged.
Q1. When Session variable remains unchanged, does the template helper not autorun even if Session.set() is called?
Q2. If so, how should I make it run every time a new image is popped?
No, Tracker computations are not invalidated if the value doesn't change.
Session.set('test', false);
Tracker.autorun(function() { console.log(Session.get('test')) }); //Logs 'false'
Session.set('test', false); //Nothing
Session.set('test', true); //Logs true
In your case, if you want to preserve this code structure (which seems a bit heavy to me) you could instead store an object with a timestamp:
if (Meteor.isClient) {
Template.TargetTask.helpers({
'imageSrc': function (e, template) {
var clock = setTimeout(function() {
var position = IMAGE_POOL.pop();
Session.set("currentTarget", {
position : position,
timestamp : Date.now()
});
}, 1000);
var position = Session.get("currentTarget").position;
var imageSrc = '/' + position + '.bmp';
return imageSrc;
}
});
}

How to make a Meteor template helper reactive without changing collection data

I'm writing a small application that shows live hits on a website. I'm displaying the hits as a table and passing each one to a template helper to determine the row's class class. The idea is that over time hits will change colour to indicate their age.
Everything renders correctly but I need to refresh the page in order to see the helper's returned class change over time. How can I make the helper work reactively?
I suspect that because the collection object's data isn't changing that this is why and I think I need to use a Session object.
Router:
Router.route('/tracked-data', {
name: 'tracked.data'
});
Controller:
TrackedDataController = RouteController.extend({
data: function () {
return {
hits: Hits.find({}, {sort: {createdAt: -1}})
};
}
});
Template:
{{#each hits}}
<tr class="{{ getClass this }}">{{> hit}}</tr>
{{/each}}
Helper:
Template.trackedData.helpers({
getClass: function(hit) {
var oneMinuteAgo = Date.now() - 1*60*1000;
if (hit.createdAt.getTime() > oneMinuteAgo) {
return 'success';
} else {
return 'error';
}
}
});
I've managed to get this working though I'm not sure it's the 'right' way to do it. I created a function that is called every second to update Session key containing the current time. Then, in my helper, I can create a new Session key for each of the objects that I want to add a class to. This session key is based upon the value in Session.get('currentTime') and thus updates every second. Session is reactive and so the template updates once the time comparison condition changes value.
var updateTime = function () {
var time = Date.now();
Session.set('currentTime', time);
setTimeout(updateTime, 1 * 1000); // 1 second
};
updateTime();
Template.trackedData.helpers({
getClass: function(hit) {
var tenMinutesAgo = Session.get('currentTime') - 10*1000,
sessionName = "class_" + hit._id,
className;
className = (hit.createdAt.getTime() > tenMinutesAgo) ? 'success' : 'error';
Session.set(sessionName, className);
return Session.get(sessionName);
}
});
Update
Thanks for the comments. The solution I ended up with was this:
client/utils.js
// Note that `Session` is only available on the client so this is a client only utility.
Utils = (function(exports) {
return {
updateTime: function () {
// Date.getTime() returns milliseconds
Session.set('currentTime', Date.now());
setTimeout(Utils.updateTime, 1 * 1000); // 1 second
},
secondsAgo: function(seconds) {
var milliseconds = seconds * 1000; // ms => s
return Session.get('currentTime') - milliseconds;
},
};
})(this);
Utils.updateTime();
client/templates/hits/hit_list.js
Template.trackedData.helpers({
getClass: function() {
if (this.createdAt.getTime() > Utils.secondsAgo(2)) {
return 'success';
} else if (this.createdAt.getTime() > Utils.secondsAgo(4)) {
return 'warning';
} else {
return 'error';
}
}
});

Meteor: Tracker.autorun and dep.changed causing infinite loop

I am using a new Tracker.Dependency for tracking several things, but it causes the autorun in the code below to run infinitely. What is wrong? The code below is okay once I separate the getSong and getSongId to depend on dep and dep2, instead of just dep.
SongManager = {
dep: new Tracker.Dependency,
dep2: new Tracker.Dependency,
init: function(songId) {
var self = this;
this.setSongId(songId);
Meteor.subscribe('song', songId);
Tracker.autorun(function(){
var songs = self.getSongCursor().fetch();
if (songs.length > 0) {
self.song = songs[0];
self.dep.changed();
}
})
},
getSongCursor: function(){
return Songs.find({_id: this.getSongId()});
},
getSong: function(){
this.dep.depend();
return this.song;
},
getSongId: function(){
this.dep2.depend();
return this.songId;
},
setSongId: function(arg){
this.songId = arg;
this.dep2.changed();
},
};
The problem is that you're creating a circular dependency. I would recommend using ReactiveVar for this rather than working with the lower-level dependency API.
meteor add reactive-var
Then you can just do this:
SongManager = {
song: new ReactiveVar(),
songId: new ReactiveVar(),
init: function(songId) {
this.songId.set(songId);
this.computation = Tracker.autorun(_.bind(this.update, this));
},
update: function() {
var songId = this.songId.get();
Meteor.subscribe('song', songId);
this.song.set(Songs.findOne(songId));
},
stop: function() {
this.computation.stop();
}
};
SongManager.init(oldSongId);
SongManager.songId.set(newSongId);
// After enough time has passed for the subscription to update and tracker to flush:
var currentSong = SongManager.song.get();
console.log(currentSong._id === newSongId); // true
I also added a way for you to stop your autorun computation so it doesn't keep running in the background when it's no longer necessary. Note that since the subscription is run within an autorun, it will automatically be stopped and restarted when the songId changes. The update function will actually be run twice, but Meteor knows not to send two identical subscription requests.

Resources