Reactive Extensions (Rx) execute periodic task - scheduled-tasks

I'm using Rx and want to execute a task periodically, let's say, every 1 minute.
My task is about to fetch, asynchronously, Feeds from an array of URLs and it's working properly:
var rssLinks = ["http://host1/rss.xml", "http://host2/rss.xml", "http://host3/rss.xml"];
// My Task
var task = Rx.Observable.from(rssLinks)
.flatMap(functions.fetchFeeds);
task.subscribe(function(feed) {
console.log("\nFeed " + feed.title + ", " + feed.link);
});
So. I've read about Scheduler and its schedulePeriodic method BUT I can't execute my task periodically.
var disposable = Rx.Scheduler.timeout.schedulePeriodic(
60000, /* 1 minute */
function () {
// Execute My Task...
});
Any ideas?
Thanks in advance!

You're just looking for Rx.Observable.interval, so something like this will do:
var taskResults = Rx.Observable.interval(60000).flatMap(function() {
return Rx.Observable.from(rssLinks).flatMap(functions.fetchFeeds);
});
taskResults.subscribe(function(x) {
// do whatever here.
});
Edit: The "what if I want rssLinks to determin my intervals" answer (from comments)
So something like this, maybe?
var rssLinks = [{ interval: 5000, url: 'http://whatever'}, { interval: 3000, url: 'https://foobar' }];
var taskResults = Rx.Observable.from(rssLinks).flatMap(function(rssLink) {
return Rx.Observable.interval(rssLink.interval).map(function(rssLink) {
return functions.fetchFeeds(rssLink.url);
});
});

Related

Element is not clickable inside of a for loop in protractor

I am using protractor-cucumber frame work with protractor 5.2.2 and mssql 3.3.0.We have a requirement of get db values,i am able to get db data values into my protractor code, But i am not able to click on a element with that db value.My code is given below.
function ConnectDB() {
var config = {
user: 'sam',
password: 'Passw0rd',
server: 'xxxxxx',
port: '50866',
database: 'testDB',
options: {
trustedConnection: true
}
}
sql.connect(config, function (err) {
if (err) console.log(err+" db connection failed");
var request = new sql.Request();
request.query('SELECT * FROM Locations WHERE ID = 2', function (err, loc){
if (err) console.log(err+" loc_name failed");
var loc_count= loc.length;
console.log(loc_count);
console.log(loc[0].Location_name);
var i;
for (i=0; i<loc_count; i++){
if(loc_count!=0 & loc_count%10==0){
element(by.css(.publish)).click();
}
element(by.cssContainingText("span", loc[i].Location_name)).click();
console.log(loc[i].Location_name);
browser.sleep(4000);
element(by.css(.post)).sendKeys("test");
element(by.css(.submit)).click();
}
});
});
}
Here the click() and senkeys() functions inside of the for loop is not working.But i am able to console the values of loc_count and loc[i].Location_name.How can i solve this?Thanks in advance.
The root cause of your issue is that the scripts of loop body almost executed async. All Protractor APIs are async. Thus the sendKeys click(), sleep() inside the loop are executed async. But the loop iterate sync.
This lead to when the i==loc_count, the click()/sendKeys()/sleep() of the i==0 have not been executed (They are async).
One solution is to use Closure to keep the i for each iteration. Another solution is use ES6 async/await.
Below is the code example of solution one:
var i;
for (i=0; i<loc_count; i++){
(function(j){
if(loc_count!=0 && loc_count%10==0){
element(by.css('.publish')).click();
}
element(by.cssContainingText("span", loc[j].Location_name)).click();
console.log(loc[j].Location_name);
browser.sleep(4000);
element(by.css('.post')).sendKeys("test");
element(by.css('.submit')).click();
})(i)
}
As yong stated it is because your for loop is synchronous and the protractor actions are async. You either need to implement async/await or use the then callbacks to make it work correctly.
Async/Await option:
request.query('SELECT * FROM Locations WHERE ID = 2', async function (err, loc){
if (err) console.log(err+" loc_name failed");
var loc_count= loc.length;
console.log(loc_count);
console.log(loc[0].Location_name);
var i;
for (i=0; i<loc_count; i++){
if(loc_count!=0 & loc_count%10==0){
await element(by.css(.publish)).click();
}
await element(by.cssContainingText("span", loc[i].Location_name)).click();
console.log(loc[i].Location_name);
await browser.sleep(4000);
await element(by.css(.post)).sendKeys("test");
await element(by.css(.submit)).click();
}
});
To use the then callbacks it would be a little more difficult to handle your if block correctly but essentially you will need to do something like this to get it to work.
element(by.cssContainingText("span", loc[i].Location_name)).click().then(() -> {
console.log(loc[i].Location_name);
browser.sleep(4000).then(() => {
element(by.css(.post)).sendKeys("test").then(() => {
element(by.css(.submit)).click();
});
});
});
The first option is far easier to read and implement correctly.
Try to use :-
var elm = element(by.id("myid"));
browser.executeScript("arguments[0].click();", elm.getWebElement());

Delete Messages with sockets

It actually should be pretty simple, but I can't seem to get a hang of it.
I want to create a chatroom with sockets.
I already managed it to send Messages, but I want to be able to delete them all.
The code for sending messages:
Server:
let io = socket(server);
io.on('connection', (socket) => {
socket.on('chat', function (data) {
io.sockets.emit('chat', data)
});
}
Client:
let socket = io.connect(window.location.protocol + '//' +
window.location.host);
socket.on('chat', function (data) {
let output: JQuery = $('#output');
let feedback: JQuery = $('#feedback');
output.html(output.html() + '<p><strong>' + data.username + ':<br>
</strong>' + data.message + '</p>');
feedback.html('');
});
function sendMessage() {
let message: JQuery = $('#message');
let username: JQuery = $('#username');
let feedback: JQuery = $('#feedback');
socket.emit('chat', {
message: message.val(),
username: username.val(),
feedback: feedback.val(),
});
message.html('');
}
$(function () {
$('#send').on('click', function () {
sendMessage();
});
}
Thank you in advance!
I already managed it to send Messages, but I want to be able to delete them all.
What do you mean by delete them? They are not stored on backend - it only broadcasting messages to clients.
EDIT
If you want user for just clear chat, according to code you provided - you can just clear output:
...
let output: JQuery = $('#output');
...
let someButton = $('#<SOME BUTTON ID>');
someButton.click(() => {
output.html('');
});
...

executeAsyncScript timing out

I am trying to execute executeAsyncScript using the following code:
function get(url) {
var callback = function(args) {
console.log(args);
};
var defer = protractor.promise.defer();
browser.executeAsyncScript(function (url, callback) {
console.log("url" + url);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
console.log(xhr.responseText);
callback(xhr.responseText);
defer.fulfill(xhr);
}
}
xhr.open('GET', url , true);
xhr.send();
}, url);
return defer.promise;
};
function setupCommon() {
return get('https://example.com/rest/api/getsomething');
}
var flow = protractor.promise.controlFlow();
flow.execute(setupCommon);
If I execute the code that is passed to executeAsyncScript directly in the browser console then it works. I get the expected output.
console.log("url" + url);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
console.log(xhr.responseText);
callback(xhr.responseText);
defer.fulfill(xhr);
}
}
xhr.open('GET', 'https://example.com/rest/api/getsomething', true);
xhr.send();
But when I execute it using executeAsyncScript, it times out saying:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
The restapi shouldn't have taken much time. I am new to all this. I am not sure what I am doing wrong. Can someone please help me with this.
The default timeout for Jasmine is 2000 milli-seconds which looks inadequate in your case as it looks like you indeed have lot of steps
Check the config file reference doc here for different timeout configurations from the protractor.conf.js
You can either increase the timeout at config level as in below
defaultTimeoutInterval: 60000,
allScriptsTimeout:90000
Or increase it for this test case alone
this.timeout(60000)
The default timeout for a script to be executed is 0ms. In most cases, including the examples below, one must set the script timeout WebDriver.Timeouts.setScriptTimeout(long, java.util.concurrent.TimeUnit) beforehand to a value sufficiently large enough.
Here is link for Java API that provides above information
https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/JavascriptExecutor.html#executeAsyncScript-java.lang.String-java.lang.Object...-

sails.js model reference error

I am studying Sails.js and I have a question.
I made a model object using "sails generate model site" command.
and I made init.js file in config directory for my cronjob.
everytime system launched, cronjob starts from this init.js.
this is init.js
var fs = require('fs'),
sails = require('sails'),
async = require('async');
exports.initSite = function () {
'use strict';
sails.log.debug('init method start!');
async.waterfall([
function (callback) {
Site.find().exec(function (err, sites) {
if (err) {
callback(err);
}
if (sites) {
async.forEachSeries(sites,
function (siteData, callback2) {
siteData.remove(function (err) {
if (err) {
callback(err);
}
callback2();
});
}, function (err) {
if (err) {
callback(err);
}
callback();
});
} else {
callback();
}
});
},
function (callback) {
var jsonData = fs.readFile('./urls.json', 'utf8', function (err, datas) {
if (err) {
callback(err);
}
callback(null, datas);
});
},
function (datas, callback) {
var urls = JSON.parse(datas);
for (var key in urls) {
var value = urls[key];
var site = new Site({
siteId: value.id,
name: value.name,
url: value.url,
category: value.category
});
site.save(function(err){
if (err) {
callback(err);
}
});
}
callback();
}
],function(err){
if (err) {
sails.log.error('ERROR!');
}
sails.log.info('INIT OK');
});
};
and I read this init.js from app.js like this.
in app.js
require(__dirname + '/config/init').initSite();
but everytime I launch application, console messages says ReferenceError: Site is not defined.
I don't know why init.js can't read 'Site (model object)'.
your advice is very thankful for me.
(sorry for my bad english.... ;_;)
Sailsjs does not load your models until it has been "lifted". You need to run sails lift first from within the directory that your app.js file has been defined, then you can run this chron job. After sails loads, all your models will be exposed as globals. The code in init.js should be called from app.js after sails lifts.
This is based on the discussion about the above topic, so it's not exactly a "cron job" but it can be executed like one and also have access to all the nice features that Sails.js provides, including models.
You can use node-schedule.
So this is what i did.
Install node-schedule
npm install –save node-schedule
Create a crontab file in config/crontab.js
Paste this code in the crontab.js. What i am doing here is creating a method where i require my cronjob, finally i append the method to the jsonArray, so the config/bootstrap.js will execute the file.
module.exports.crontab = {
/*
* The asterisks in the key are equivalent to the
* schedule setting in crontab, i.e.
* minute hour day month day-of-week year
* so in the example below it will run every minute
*/
crons:function()
{
var jsonArray = [];
jsonArray.push({interval:’*/1 * * * * * ‘,method:’mytest’});
// add more cronjobs if you want like below
// but dont forget to add a new method…
//jsonArray.push({interval:’*/1 * * * * * ‘,method:’anothertest’});
return jsonArray;
},
// declare the method mytest
// and add it in the crons function
mytest: function(){
require(‘../crontab/mytest.js’).run();
}
/*
anothertest:function(){
require(‘../crontab/anothertest.js’).run();
}
*/
};
Open your config/bootstrap.js and add this code
module.exports.bootstrap = function(cb) {
_.extend(sails.hooks.http.app.locals, sails.config.http.locals);
// add the lines from here
// bootstrapping all the cronjobs in the crontab folder
var schedule = require(‘node-schedule’);
sails.config.crontab.crons().forEach(function(item){
schedule.scheduleJob(item.interval,sails.config.crontab[item.method]);
});
// It’s very important to trigger this callback method when you are finished
// with the bootstrap! (otherwise your server will never lift, since it’s waiting on the bootstrap)
cb();
};
Finally run sails l and you should see a message running every minute.

Backbone.js Model different url for create and update?

lets say I have a Backbone Model and I create an instance of a model like this:
var User = Backbone.Model.extend({ ... });
var John = new User({ name : 'John', age : 33 });
I wonder if it is possible when I use John.save() to target /user/create when I use John.save() on second time (update/PUT) to target /user/update when I use John.fetch() to target /user/get and when I use John.remove() to target /user/remove
I know that I could define John.url each time before I trigger any method but I'm wondering if it could be happen automatically some how without overriding any Backbone method.
I know that I could use one url like /user/handle and handle the request based on request method (GET/POST/PUT/DELETE) but I'm just wondering if there is a way to have different url per action in Backbone.
Thanks!
Methods .fetch(), .save() and .destroy() on Backbone.Model are checking if the model has .sync() defined and if yes it will get called otherwise Backbone.sync() will get called (see the last lines of the linked source code).
So one of the solutions is to implement .sync() method.
Example:
var User = Backbone.Model.extend({
// ...
methodToURL: {
'read': '/user/get',
'create': '/user/create',
'update': '/user/update',
'delete': '/user/remove'
},
sync: function(method, model, options) {
options = options || {};
options.url = model.methodToURL[method.toLowerCase()];
return Backbone.sync.apply(this, arguments);
}
}
To abstract dzejkej's solution one level further, you might wrap the Backbone.sync function to query the model for method-specific URLs.
function setDefaultUrlOptionByMethod(syncFunc)
return function sync (method, model, options) {
options = options || {};
if (!options.url)
options.url = _.result(model, method + 'Url'); // Let Backbone.sync handle model.url fallback value
return syncFunc.call(this, method, model, options);
}
}
Then you could define the model with:
var User = Backbone.Model.extend({
sync: setDefaultUrlOptionByMethod(Backbone.sync),
readUrl: '/user/get',
createUrl: '/user/create',
updateUrl: '/user/update',
deleteUrl: '/user/delete'
});
Are you dealing with a REST implementation that isn't to spec or needs some kind of workaround?
Instead, consider using the emulateHTTP option found here:
http://documentcloud.github.com/backbone/#Sync
Otherwise, you'll probably just need to override the default Backbone.sync method and you'll be good to go if you want to get real crazy with that... but I don't suggest that. It'd be best to just use a true RESTful interface.
No you can't do this by default with backbone. What you could to is to add to the model that will change the model url on every event the model trigger. But then you have always the problem that bckbone will use POST add the first time the model was saved and PUT for every call afterward. So you need to override the save() method or Backbone.sync as well.
After all it seems not a good idea to do this cause it break the REST pattern Backbone is build on.
I got inspired by this solution, where you just create your own ajax call for the methods that are not for fetching the model. Here is a trimmed down version of it:
var Backbone = require("backbone");
var $ = require("jquery");
var _ = require("underscore");
function _request(url, method, data, callback) {
$.ajax({
url: url,
contentType: "application/json",
dataType: "json",
type: method,
data: JSON.stringify( data ),
success: function (response) {
if ( !response.error ) {
if ( callback && _.isFunction(callback.success) ) {
callback.success(response);
}
} else {
if ( callback && _.isFunction(callback.error) ) {
callback.error(response);
}
}
},
error: function(mod, response){
if ( callback && _.isFunction(callback.error) ) {
callback.error(response);
}
}
});
}
var User = Backbone.Model.extend({
initialize: function() {
_.bindAll(this, "login", "logout", "signup");
},
login: function (data, callback) {
_request("api/auth/login", "POST", data, callback);
},
logout: function (callback) {
if (this.isLoggedIn()) {
_request("api/auth/logout", "GET", null, callback);
}
},
signup: function (data, callback) {
_request(url, "POST", data, callback);
},
url: "api/auth/user"
});
module.exports = User;
And then you can use it like this:
var user = new User();
// user signup
user.signup(data, {
success: function (response) {
// signup success
}
});
// user login
user.login(data, {
success: function (response) {
// login success
}
});
// user logout
user.login({
success: function (response) {
// logout success
}
});
// fetch user details
user.fetch({
success: function () {
// logged in, go to home
window.location.hash = "";
},
error: function () {
// logged out, go to signin
window.location.hash = "signin";
}
});