Durandaljs pubsub - events

I would like to ask if there is a way to unsubscribe a specific subscriber using the durandal pubsub mechanism.
For example; Let's say i have the following subscription:
app.on('some topic')
.then(function(){ ... });
But when i do:
app.off('some topic')
it ends up removing all the subscribers for the specific topic.
Instead, i would like to do something the following:
var mySubscriber = function( msg, data ){
console.log( msg, data );
};
var token = PubSub.subscribe( 'MY MESSAGE', mySubscriber );
PubSub.unsubscribe( token );
(The above snippet is taken from the PubSubJs library examples)

var subscribeTopic = app.on('some topic')
.then(function(){ ... });
subscribeTopic.off();
http://durandaljs.com/documentation/Leveraging-Publish-Subscribe/

Related

How do I instantiate a turnContext using the adapter and request as parameters

I would like to instantiate a turnContext to be used in integration testing. How would I be able to instantiate one without calling on the processActivity() method of the adapter?
I am looking at the documentation but it shows that I would need the request of the post call as the parameter. I would like my testing to be independant of the post call. I would then assume that I would need to instantiate the request? How would I go about doing so?
Image of documentation
This is a bit hard to answer without knowing how you are planning to use the code. That being said, it's not that hard to create a new turnContext and also bypass the processActivity(). Given how you are referencing turnContext and processActivity(), I'm assuming you are using the Node SDK. Implementing in C# wouldn't be too different.
Here are two options, both utilizing the creation of a new adapter, however you can also pass in an already established turnContext, if desired:
Use .createContext in server.post in the index.js file, or
Maintain the processActivity() method in the server.post. This calls a new "onTurn" method in the bot.js file. In doing so, this allows you to control when and how the new "onTurn" is accessed.
Option 1: In the index.js file, you will want to create a new adapter or make a copy of the first depending on your needs:
const adapter = new BotFrameworkAdapter({
appId: endpointConfig.appId || process.env.MicrosoftAppId,
appPassword: endpointConfig.appPassword || process.env.MicrosoftAppPassword
});
const newAdapter = adapter;
or
const adapter = new BotFrameworkAdapter({
appId: endpointConfig.appId || process.env.MicrosoftAppId,
appPassword: endpointConfig.appPassword || process.env.MicrosoftAppPassword
});
const newAdapter = new BotFrameworkAdapter({
appId: endpointConfig.appId || process.env.MicrosoftAppId,
appPassword: endpointConfig.appPassword || process.env.MicrosoftAppPassword
});
Include the onTurnError code to catch errors:
// Catch-all for errors.
adapter.onTurnError = async (context, error) => {
console.error(`\n [onTurnError]: ${ error }`);
await context.sendActivity(`Oops. Something went wrong!`);
};
// Catch-all for errors.
newAdapter.onTurnError = async (context, error) => {
console.error(`\n [onTurnError]: ${ error }`);
await context.sendActivity(`Oops. Something went wrong!`);
};
Then, set the new adapters and create the new turnContext:
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (turnContext) => {
await bot.onTurn(turnContext);
});
newAdapter.createContext(req, res);
});
Options 2: In the index.js file, building off of the above code, set the adapters to await the individual "onTurn" methods:
// Listen for incoming requests.
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async (turnContext) => {
await bot.onTurn(turnContext);
});
newAdapter.processActivity(req, res, async (turnContext) => {
await bot.newOnTurn(turnContext);
});
});
In the bot.js file, you will have your two "onTurn" methods. In this example, the different "onTurn" methods are called based on whether a message is sent or I am deleting user data (I am sending this event via the Emulator => Conversation menu item). What you decide to match on is up to you.
async newOnTurn(turnContext) {
if (turnContext.activity.type === ActivityTypes.DeleteUserData) {
const dc = await this.dialogs.createContext(turnContext);
await dc.context.sendActivity(`Looks like you deleted some user data.`);
}
}
async onTurn(turnContext) {
if (turnContext.activity.type === ActivityTypes.Message) {
const dc = await this.dialogs.createContext(turnContext);
await dc.context.sendActivity(`Looks like you sent a message.`);
}
}
Hope of help!

Socket.io sends two messages

I'm trying to setup socket.io and here is part of my server.js
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http, { path: '/websocket', origins:'*:*' });
io.on('connection', (socket) => {
socket.send('Hi');
socket.on('message', (message) => {
console.log(message);
socket.emit('hello', `New: ${message}`);
});
console.log('a user connected');
});
http.listen(3030, function(){
console.log('listening on *:3030');
});
and my simple client:
var socket = io('https://*******.com', {
secure: true,
path: '/websocket'
});
const input = document.getElementById('text');
const button = document.getElementById('button');
const msg = document.getElementById('msg');
button.onclick = () => {
socket.emit('message', input.value);
socket.on('hello', (text) => {
const el = document.createElement('p');
el.innerHTML = text;
msg.appendChild(el);
})
}
And if I'll click for third time I receive a 3 messages back and so on. What I'm doing wrong? I wish to send message to the server and receive modified message back.
I'm new in web sockets.
Any help appreciated.
P.S. socket.io v2.0.1
You are adding a socket.on() event handler each time the button is clicked. So, after the button has been clicked twice, you have duplicate socket.on() event handlers. When the event comes back, your two event handlers will each get called and you will think you are getting duplicate messages. Actually, it's just one message, but with duplicate event handlers.
You pretty much never want to add an event handler inside another event handler because that leads to this sort of build-up of duplicate event handlers. You don't describe (in words) exactly what you're code is trying to do so I don't know exactly what alternative to suggest. Usually, you set up the event handlers first, just once, when the socket is connected and then you will never get duplicate handlers.
So, perhaps it's as simple as changing this:
button.onclick = () => {
socket.emit('message', input.value);
socket.on('hello', (text) => {
const el = document.createElement('p');
el.innerHTML = text;
msg.appendChild(el);
})
}
to this:
button.onclick = () => {
socket.emit('message', input.value);
}
socket.on('hello', (text) => {
const el = document.createElement('p');
el.innerHTML = text;
msg.appendChild(el);
});
If you are using Angular and (probably) embedding the Socket in a Service (simpleton) you are creating a persistent listener in ngOnInit every time you load a page.
You need to create some kind of flag to know if the listener was already created in the Service from another instance of your page.

socket.io - how to access unhandled messages?

How can you detect that you received a message on a socket.io connection that you do not have a handler for?
example:
// client
socket.emit('test', 'message');
// server
io.on('connection', function (socket) {
console.log('connection received...');
// logs all messages
socket.conn.on('message', function(data) {
console.log('this gets every message.');
console.log('how do I get just the ones without explicit handlers?');
});
socket.on('other' function(data) {
console.log('expected message');
});
}
By accessing the internals of the socket object you can determine what events it is currently listening for. You can use this server-side code to see if the current message is being handled.
io.on('connection', (socket) => {
console.log('A user connected.');
socket.on('disconnect', () => {
console.log('A user disconnected.');
});
socket.on('chat', (msg) => {
console.log('message: ' + msg);
io.emit('chat', msg);
});
socket.conn.on('message', (msg) => {
if(!Object.keys(socket._events).includes(msg.split('"')[1])) {
console.log(`WARNING: Unhandled Event: ${msg}`);
}
});
}
Once connected I am handling two events, 'disconnect' and 'chat'. After that I define a handler that catches all messages with socket.conn.on(...).
The message it receives is going to be a string that looks something like this: '2["myEventName","param1","param2"]'. By splitting it along the double quotes we can get the event name.
We then peek into the internals of socket to find all the keys of socket._events, which happen to be the event name strings. If this collection of strings includes our event name, then another handler will take care of it, and we don't have to.
You can test it from the console in the browser. Run socket.emit('some other event') there and you should see your warning come up in the server console.
IMPORTANT NOTE: Normally you should not attempt to externally modify any object member starting with an underscore. Also, expect that any data in it is unstable. The underscore indicates it is for internal use in that object, class or function. Though this object is not stable, it should be up to date enough for us to use it, and we aren't modifying it directly.
Tested with SocketIO version 2.2.0 on Chrome.
I didn't find a way to do it like socket.io, but using a simple js function to transform message into json it's doing the same job. Here you can try this:
function formatMessage(packetType, data) {
var message = {'packetType': packetType, 'data': data}
return JSON.stringify(message)
}
With:
socket.on('message', function(packet){
packet = JSON.parse(packet)
switch (packet.packetType) {
case 'init':
...
and
socket.send(formatMessage('init', {message}));
I would do so, of course it is the abstract code ... you would have to implement all the listeners and the logic to get the ids of the users to work
Client
var currentUser = {
id: ? // The id of current user
};
var socketMessage = {
idFrom: currentUser.id,
idTo: ?, // Some user id value
message: 'Hello there'
};
socket.emit('message', socketMessage);
socket.on('emitedMessage' + currentUser.id, function(message) {
// TODO: handle message
});
Server
io.on('connection', function (socket) {
// Handle emit messages
socket.on('message', function(socketMessage) {
// With this line you send the message to a specific user
socket.emit('emitedMessage-' + socketMessage.idTo, {
from: socketMessage.idFrom,
message: socketMessage.message
});
});
});
More info: http://socket.io/docs/

MS Bot Framework (Nodejs): Session lifetime and delayed messages

This is my first try with Bot Framework (Nodejs). I want to test delayed messages, for example, my bot must answer after a 5 seconds after receiving the message.
So I tried with this code:
var builder = require('botbuilder');
var connector = new builder.consoleconnector().listen();
var bot = new builder.universalbot(connector);
bot.dialog('/', function (session) {
if (!session.userData.TimeoutStarted) {
session.send("I'll answer in 5 seconds");
session.userData.TimeoutStarted = true;
setTimeout(function() {
session.send("Answer after 5 seconds");
session.userData.TimeoutStarted = false;
}, 5000);
} else {
session.send("Bot is busy");
}
});
But this doesn't work. Callback function inside setTimeout fires, but all operations with session doesn't work at all.
So, I find possible solution here: How to send message later in bot framework and rewrite my code:
var builder = require('botbuilder');
var connector = new builder.ConsoleConnector().listen();
var bot = new builder.UniversalBot(connector);
bot.dialog('/', function (session) {
if (session.userData.Timeout > 0 && Date.now() - session.userData.Timeout > 5000)
session.userData.Timeout = 0;
if (!session.userData.Timeout) {
session.send("I'll answer in 5 seconds");
var reply = session.message;
setTimeout(function() {
reply.text = "Answer after 5 seconds";
bot.send(reply);
}, 5000);
session.userData.Timeout = Date.now();
} else {
session.send("Bot is busy");
}
});
This code works, but looks terrible with so many checks. So I have a few questions:
Why first code example doesn't work? I guess problem in the session lifetime and then what is session lifetime?
How to set session.userData in this examples? So In first code example I want to set it inside callback function inside setTimeout but it doesn't work too.
What is the best way to create delayed answers?
I just investigated this issue. Looks like there's a bug in ConsoleConnector that makes it impossible to send two messages using the same session object (above a given interval between messages, due to internal batching). As the state is also persisted during send, your delayed state update will also not work. If you added a call to session.save to your callback in setTimeout, it would persist the new state (but would still not send the message).
I believe your first example should work with ChatConnector (haven't had the chance to try though). I'll create a pull request with the fix to ConsoleConnector.
I hope this answers all your questions.
UPDATE
See this issue and the related pull request for more details.
UPDATE2
It works for me with ChatConnector, using this code:
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
var connector = new builder.ChatConnector({
appId: '',
appPassword: ''
});
var bot = new builder.UniversalBot(connector);
server.post('/api/messages', connector.listen());
bot.dialog('/', function (session) {
if (!session.userData.TimeoutStarted) {
session.send("I'll answer in 5 seconds");
session.userData.TimeoutStarted = true;
setTimeout(function() {
session.send("Answer after 5 seconds");
session.userData.TimeoutStarted = false;
}, 5000);
} else {
session.send("Bot is busy");
}
});
For people who are interested in sending a delayed message from bot, you could use session.delay(<ms>)
For example,
session.send('msg')
session.delay(5000) // delay 5 seconds
session.endDialog()

How to Mock and test using an RxJS subject?

I have some functions that accept an RxJS subject (backed to a socket) that I want to test. I'd like to mock the subject in a very request reply fashion. Since I'm unsure of a clean Rx way to do this, I'm tempted to use an EventEmitter to form my fake socket.
Generally, I want to:
check that the message received on my "socket" matches expectations
respond to that message on the same subject: observer.next(resp)
I do need to be able to use data from the message to form the response as well.
The code being tested is
export function acquireKernelInfo(sock) {
// set up our JSON payload
const message = createMessage('kernel_info_request');
const obs = shell
.childOf(message)
.ofMessageType('kernel_info_reply')
.first()
.pluck('content', 'language_info')
.map(setLanguageInfo)
.publishReplay(1)
.refCount();
sock.next(message);
return obs;
}
You could manually create two subjects and "glue them together" as one Subject with Subject.create:
const sent = new Rx.Subject();
const received = new Rx.Subject();
const mockWebSocketSubject = Subject.create(sent, received)
const s1 = sent.subscribe(
(msg) => sentMsgs.push({ next: msg }),
(err) => sentMsgs.push({ error: err }),
() => sendMsgs.push({ complete: true })
);
const s2 = recieved.subscribe(
(msg) => sentMsgs.push({ next: msg }),
(err) => sentMsgs.push({ error: err }),
() => sendMsgs.push({ complete: true })
);
// to send a message
// (presumably whatever system you're injecting this into is doing the sending)
sent.next('weee');
// to mock a received message
received.next('blarg');
s1.unsubscribe();
s2.unsubscribe();
That said, it's really a matter of what you're testing, how it's structured, and what the API is.
Ideally you'd be able to run your whole test synchronously. If you can't for some Rx-related reason, you should look into the TestScheduler, which has facilities to run tests in virtualized time.

Resources