How listen for events in Hyperledger Fabric Composer? - hyperledger-composer

I haven't received any event notifications and am wondering if I am missing something.
I followed the instructions from the Fabric Composer website to define the BasicEvent in my cto model and added the code for creating and emitting events in a transaction and updated the network. I created a separate eventListener.js program that subscribes to the events using the businessNetworkConnection using the code sample from the website.
After I start my eventListener.js app it seems to be listening (after receiving the connected status message in console, nothing else happens...it doesn't go back to the normal prompt line.)
I then execute the transaction that should emit the event and it runs successfully but no event is received in the other terminal window where the eventlistener.js is running.
Here is the key part of the eventListener.js program:
businessNetworkConnection.connect(connectionProfile, businessNetworkIdentifier, participantId, participantPwd)
.then((result) => {
businessNetworkDefinition = result;
console.log('Connected: BusinessNetworkDefinition obtained=' + businessNetworkDefinition.getIdentifier());
});
businessNetworkConnection.on('event', (event) => {
// event: { "$class": "org.namespace.BasicEvent", "eventId": "0000-0000-0000-000000#0" }
console.log(event);
});
Is the businessNetworkConnection.on('event', (event) ... command supposed to cause the program to appear to hang while its listening?
If so, is there something else that could be done to troubleshoot where the problem is?
I'm using v0.6 HLF on local Docker.

I think your issue is that you don't wait for the businessNetworkDefinition to be connected before you register your listener. Remember that the then block is executed asynchronously and your following businessNetworkConnection.on code will execute immediately.
You should add a second then block to register your listener after the businessNetworkDefinition has been connected.
e.g.
return adminConnection.connect('hlfv1', 'admin', 'adminpw')
.then(() => {
return BusinessNetworkDefinition.fromDirectory(path.resolve(__dirname, '..'));
})
.then((businessNetworkDefinition) => {
return adminConnection.deploy(businessNetworkDefinition);
// return true;
})
.then(() => {
businessNetworkConnection = new BusinessNetworkConnection();
return businessNetworkConnection.connect('hlfv1', 'my-network', 'admin', 'adminpw');
})
.then(() => {
businessNetworkConnection.on('event', (event) => {
console.log( '****** received the event ' + JSON.stringify(businessNetworkConnection.getBusinessNetwork().getSerializer().toJSON(event)));
});
});

I wrote a sample code for a basic sample application developed by Hyperledger team. You can check out about from this code snippet: SampleEventListener

Related

Svelte/Sveltekit and socket.io-client not working in dev (works in preview)

I'm trying to make socket.io-client work in a svelte front end app to talk to an existing API server that already uses socket.io. After a number of challenges, I managed to make this work but I can only get this to work with sveltekit's preview and not in dev mode. Wondered if someone with some knowledge of those could explain why or suggest what I need to do to get it connecting in dev?
svelte 3.34.0
sveltekit next-169
socket.io(-client) 4.2.0
basic code as follows, currently within a file $lib/db.js where I define a few stores that are pulled into the layout for general use..
import { io } from "socket.io-client";
import { browser } from '$app/env';
const initSocket = async () => {
console.log('creating socket...');
let socket = io('http://192.168.1.5:4000', { 'connect timeout': 5000 });
socket.on("connect", () => {
// always works in preview...
console.log('socket created with ID:', socket.id);
});
socket.on("connect_error", (error) => {
// permanently fired in dev...
console.error('Failed to connect', error);
});
socket.on("error", (error) => {
console.error('Error on socket', error);
});
socket.on("foo", data => {
// works in preview when server emits a message of type 'foo'..
console.log("FOO:", data);
});
};
if (browser) {
initSocket();
}
// stores setup and exports omitted..
with svelte-kit preview --host I see the socket creation log message with the socket ID and the same can be seen on the api server where it logs the same ID. The socket works and data is received as expected.
with svelte-kit dev --host however, the log message from socket.on("connect").. is never output and I just see an endless stream of error messages in the browser console from the socket.on("connect_error").. call..
Failed to connect Error: xhr poll error
at XHR.onError (transport.js:31)
at Request.<anonymous> (polling-xhr.js:93)
at Request.Emitter.emit (index.js:145)
at Request.onError (polling-xhr.js:242)
at polling-xhr.js:205
Importantly, there is no attempt to actually contact the server at all. The server never receives a connection request and wireshark/tcpdump confirm that no packet is ever transmitted to 192.168.1.5:4000
Obviously having to rebuild and re-run preview mode on each code change makes development pretty painful, does anyone have insight as to what the issue is here or suggestions on how to proceed?
I've had a similar problem, I solved it by adding this code to svelte.config.js:
const config = {
kit: {
vite: {
resolve: {
alias: {
"xmlhttprequest-ssl": "./node_modules/engine.io-client/lib/xmlhttprequest.js",
},
},
},
},
};
The solution was provided by this comment from the vite issues.

Exit Event / Welcome Event Not Firing

We're trying to get some events/messages to post when a user exits a chatbot window (or the site) (or a welcome message), but so far the events are not firing.
I can see within Inspector tools:
Screen Shot 2020-02-18 at 3 15 39 PM
Various activities/conversations are created, the chatbot works, but no welcome/exit events are triggered.
The code we're using is nearly if not identical to documentation code here: https://github.com/microsoft/BotFramework-WebChat/blob/master/docs/WELCOME_MESSAGE.md
and here: How to handle user leaving conversation
I have a function that fires when the window is closed, as follows:
const store = window.WebChat.createStore( {}, ( { dispatch } ) => next => async action => {
return next( action );});
window.addEventListener( 'sendEventActivity', ( { data } ) => {
store.dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'user_event',
value: {
name: 'end_conversation',
value: 'user ended conversation'
},
text: 'The user has left the conversation.'
}
})
});
function exitEvent(){
const eventSendActivity = new Event( 'sendEventActivity' );
eventSendActivity.data = 'User left conversation';
window.dispatchEvent( eventSendActivity );
console.log('Exit Event Submitted (hopefully)');
}
exitEvent();
I have tried other variations, defining the store earlier, above render chat, after render chat, sending welcome messages from various locations and at various times but can't seem to get it to send.
We are using https://cdn.botframework.com/botframework-webchat/latest/webchat.js
Any idea what the issue might be? Not sure where we are going wrong or why it's not firing - copying in theory known to be working code straight into our code doesn't seem to do the trick.
Thanks in advance and please let me know if I have failed to include any necessary details- new to chatbot and do not post much on github. Many thanks,
EDIT:
I was able to marry the aforementioned code and code from here: https://github.com/microsoft/BotFramework-WebChat/issues/2120#issuecomment-516056614 in order to achieve what I wanted. I'll post below in case it helps anyone else...
const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/join'
}
});
}
return next(action);
});
window.addEventListener( 'sendEventActivity', ( { data } ) => {
store.dispatch( {
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/exit'
}
} );
} );
document.getElementById("action_menu_btn").addEventListener( 'click', function() {
const eventSendActivity = new Event( 'sendEventActivity' );
eventSendActivity.data = 'User left conversation';
window.dispatchEvent( eventSendActivity );
console.log('End Converstaion Event Fired');
});
Cheers!
I failed to mention this in the other post (I'll update it), but the reason the code works is because of the window.onbeforeunload() function. Without it, the window closes before any code can finish executing. The result being no event is created, is caught by a listener, nor is sent via the Web Chat store to the bot.
Here, using the above, refreshing the page produces the "User left conversation" activity.
Also, something to note, any function you create and pass thru like you have with exitEvent() is going to run as soon as the page loads. Take the following code which gets the user's location via the browser (placed just before the closing </script> tag). As you can see, it's loading even before Web Chat. If you are wanting a function to run according to some activity passed from the bot, then utilize either the store's actions (i.e. DIRECT_LINE/INCOMING_ACTIVITY, or some other) or via the available middleware.
let geoLoc = async () => {
await navigator.geolocation.getCurrentPosition(position => {
console.log('Latitude: ', position.coords.latitude);
console.log('Longitude: ', position.coords.longitude);
});
}
geoLoc();
Regarding a welcome message, you have two options. Either send as an activity from your bot (reference this sample) or initiate an event on your page after some initial activity is received (reference this sample).
Lastly, I would recommend getting the code working as-is before tinkering with it. This usually trips me up, so thought I'd pass it along.
Hope of help!

How to listen global events with Cypress?

We have an application that polls the server periodically until a task is completed. We fire a global event so that Cypress can catch and find out if the task is finished but we had trouble using document.addEventListener on Cypress. Here's what we're doing:
document.addEventListener('queryEnd', () => {
cy.get('.chart').should('be.visible')
cy.get('.table').should('be.visible')
})
However; when we use it in a spec, it doesn't work expected and we're not able to catch it. Also, Cypress doesn't wait for the test and runs afterEach without waiting for the callback to run.
The reason why your code isn't working like you expect is because in Cypress, your tests run in a separate frame than your application under test (AUT). The event you're waiting for will never fire inside of Cypress's document.
To get the document of the AUT, use cy.document() like this:
cy.document()
.then($document => {
// now $document is a reference to the AUT Document
$document.addEventListener(...)
})
To make Cypress wait for your event before continuing, you can just wrap it in a Cypress.Promise. The Cypress docs have an example about waiting for a Promise to complete. For your queryEnd event, it would look something like this:
cy.document() // get a handle for the document
.then($document => {
return new Cypress.Promise(resolve => { // Cypress will wait for this Promise to resolve
const onQueryEnd = () => {
$document.removeEventListener('queryEnd', onQueryEnd) // cleanup
resolve() // resolve and allow Cypress to continue
}
$document.addEventListener('queryEnd', onQueryEnd)
})
})
.then(() => {
cy.get('.chart').should('be.visible')
cy.get('.table').should('be.visible')
})

Using asynchronous Nightwatch After Hook with client interaction does not work

As far as I can tell, using promises or callbacks in After hook prevents Command Queue from executing when using promises / callbacks. I'm trying to figure out why, any help or suggestions are appreciated. Closest issue I could find on github is: https://github.com/nightwatchjs/nightwatch/issues/341
which states: finding that trying to make browser calls in the after hook is too late; it appears that the session is closed before after is run. (exactly my problem). But there is no solution provided. I need to run cleanup steps after my scenarios run, and those cleanup steps need to be able to interact with browser.
https://github.com/nightwatchjs/nightwatch/wiki/Understanding-the-Command-Queue
In the snippet below, bar is never outputted. Just foo.
const { After } = require('cucumber');
const { client } = require('nightwatch-cucumber');
After(() => new Promise((resolve) => {
console.log('foo')
client.perform(() => {
console.log('bar')
});
}));
I also tried using callback approach
After((browser, done) => {
console.log('foo');
client.perform(() => {
console.log('bar');
done();
});
});
But similar to 1st example, bar is never outputted, just foo
You can instead use something like:
const moreWork = async () => {
console.log('bar');
await new Promise((resolve) => {
setTimeout(resolve, 10000);
})
}
After(() => client.perform(async () => {
console.log('foo');
moreWork();
}));
But the asynchronous nature of moreWork means that the client terminates before my work is finished, so this isn't really workin for me. You can't use an await in the perform since they are in different execution contexts.
Basically the only way to get client commands to execute in after hook is my third example, but it prevents me from using async.
The 1st and 2nd examples would be great if the command queue didn't freeze and prevent execution.
edit: I'm finding more issues on github that state the browser is not available in before / after hooks: https://github.com/nightwatchjs/nightwatch/issues/575
What are you supposed to do if you want to clean up using the browser after all features have run?
Try the following
After(async () => {
await client.perform(() => {
...
});
await moreWork();
})

.Net Core SignalR - connection timeout - heartbeat timer - connection state change handling

just to be clear up-front, this questions is about .Net Core SignalR, not the previous version.
The new SignalR has an issue with WebSockets behind IIS (I can't get them to work on Chrome/Win7/IIS express). So instead I'm using Server Sent Events (SSE).
However, the problem is that those time out after about 2 minutes, the connection state goes from 2 to 3. Automatic reconnect has been removed (apparently it wasn't working really well anyway in previous versions).
I'd like to implement a heartbeat timer now to stop clients from timing out, a tick every 30 seconds may well do the job.
Update 10 November
I have now managed to implement the server side Heartbeat, essentially taken from Ricardo Peres' https://weblogs.asp.net/ricardoperes/signalr-in-asp-net-core
in startup.cs, add to public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
app.UseSignalR(routes =>
{
routes.MapHub<TheHubClass>("signalr");
});
TimerCallback SignalRHeartBeat = async (x) => {
await serviceProvider.GetService<IHubContext<TheHubClass>>().Clients.All.InvokeAsync("Heartbeat", DateTime.Now); };
var timer = new Timer(SignalRHeartBeat).Change(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(30));
HubClass
For the HubClass, I have added public async Task HeartBeat(DateTime now) => await Clients.All.InvokeAsync("Heartbeat", now);
Obviously, both the timer, the data being sent (I'm just sending a DateTime) and the client method name can be different.
Update .Net Core 2.1+
See the comment below; the timer callback should no longer be used. I've now implemented an IHostedService (or rather the abstract BackgroundService) to do that:
public class HeartBeat : BackgroundService
{
private readonly IHubContext<SignalRHub> _hubContext;
public HeartBeat(IHubContext<SignalRHub> hubContext)
{
_hubContext = hubContext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _hubContext.Clients.All.SendAsync("Heartbeat", DateTime.Now, stoppingToken);
await Task.Delay(30000, stoppingToken);
}
}
}
In your startup class, wire it in after services.AddSignalR();:
services.AddHostedService<HeartBeat>();
Client
var connection = new signalR.HubConnection("/signalr", { transport: signalR.TransportType.ServerSentEvents });
connection.on("Heartbeat", serverTime => { console.log(serverTime); });
Remaining pieces of the initial question
What is left is how to properly reconnect the client, e.g. after IO was suspended (the browser's computer went to sleep, lost connection, changed Wifis or whatever)
I have implemented a client side Heartbeat that is working properly, at least until the connection breaks:
Hub Class: public async Task HeartBeatTock() => await Task.CompletedTask;
Client:
var heartBeatTockTimer;
function sendHeartBeatTock() {
connection.invoke("HeartBeatTock");
}
connection.start().then(args => {
heartBeatTockTimer = setInterval(sendHeartBeatTock, 10000);
});
After the browser suspends IO for example, the invoke method would throw an exception - which cannot be caught by a simple try/catch because it is async.
What I tried to do for my HeartBeatTock was something like (pseudo-code):
function sendHeartBeatTock
try connection.invoke("HeartbeatTock)
catch exception
try connection.stop()
catch exception (and ignore it)
finally
connection = new HubConnection().start()
repeat try connection.invoke("HeartbeatTock")
catch exception
log("restart did not work")
clearInterval(heartBeatTockTimer)
informUserToRefreshBrowser()
Now, this does not work for a few reasons. invoke throws the exception after the code block executes due to being run asynchronous. It looks as though it exposes a .catch() method, but I'm not sure how to implement my thoughts there properly.
The other reason is that starting a new connection would require me to re-implement all server calls like "connection.on("send"...) - which appears silly.
Any hints as to how to properly implement a reconnecting client would be much appreciated.
This is an issue when running SignalR Core behind IIS. IIS will close idle connections after 2 minutes. The long term plan is to add keep alive messages which, as a side effect, will prevent IIS from closing the connection. To work around the problem for now you can:
send periodically a message to the clients
change the idle-timeout setting in IIS as described here
restart the connection on the client side if it gets closed
use a different transport (e.g. long polling since you cannot use webSockets on Win7/Win2008 R2 behind IIS)
I've got a working solution now (tested in Chrome and FF so far). In the hope to either motivate you to come up with something better, or to save you a little while coming up with something like this yourselves, I'm posting my solution here:
The Heartbeat-"Tick" message (the server routinely pinging the clients) is described in the question above.
The client ("Tock" part) now has:
a function to register the connection, so that the callback methods (connection.on()) can be repeated; they'd be lost after just restarting a "new HubConnection" otherwise
a function to register the TockTimer
and a function to actually send Tock pings
The tock method catches errors upon sending, and tries to initiate a new connection. Since the timer keeps running, I'm registering a new connection and then simply sit back and wait for the next invocation.
Putting the client together:
// keeps the connection object
var connection = null;
// stores the ID from SetInterval
var heartBeatTockTimer = 0;
// how often should I "tock" the server
var heartBeatTockTimerSeconds = 10;
// how often should I retry after connection loss?
var maxRetryAttempt = 5;
// the retry should wait less long then the TockTimer, or calls may overlap
var retryWaitSeconds = heartBeatTockTimerSeconds / 2;
// how many retry attempts did we have?
var currentRetryAttempt = 0;
// helper function to wait a few seconds
$.wait = function(miliseconds) {
var defer = $.Deferred();
setTimeout(function() { defer.resolve(); }, miliseconds);
return defer;
};
// first routine start of the connection
registerSignalRConnection();
function registerSignalRConnection() {
++currentRetryAttempt;
if (currentRetryAttempt > maxRetryAttempt) {
console.log("Clearing registerHeartBeatTockTimer");
clearInterval(heartBeatTockTimer);
heartBeatTockTimer = 0;
throw "Retry attempts exceeded.";
}
if (connection !== null) {
console.log("registerSignalRConnection was not null", connection);
connection.stop().catch(err => console.log(err));
}
console.log("Creating new connection");
connection = new signalR.HubConnection("/signalr", { transport: signalR.TransportType.ServerSentEvents });
connection.on("Heartbeat", serverTime => { console.log(serverTime); });
connection.start().then(() => {
console.log("Connection started, starting timer.");
registerHeartBeatTockTimer();
}).catch(exception => {
console.log("Error connecting", exception, connection);
});
}
function registerHeartBeatTockTimer() {
// make sure we're registered only once
if (heartBeatTockTimer !== 0) return;
console.log("Registering registerHeartBeatTockTimer");
if (connection !== null)
heartBeatTockTimer = setInterval(sendHeartBeatTock, heartBeatTockTimerSeconds * 1000);
else
console.log("Connection didn't allow registry");
}
function sendHeartBeatTock() {
console.log("Standard attempt HeartBeatTock");
connection.invoke("HeartBeatTock").then(() => {
console.log("HeartbeatTock worked.") })
.catch(err => {
console.log("HeartbeatTock Standard Error", err);
$.wait(retryWaitSeconds * 1000).then(function() {
console.log("executing attempt #" + currentRetryAttempt.toString());
registerSignalRConnection();
});
console.log("Current retry attempt: ", currentRetryAttempt);
});
}
Client version based on ExternalUse's answer...
import * as signalR from '#aspnet/signalr'
import _ from 'lodash'
var connection = null;
var sendHandlers = [];
var addListener = f => sendHandlers.push(f);
function registerSignalRConnection() {
if (connection !== null) {
connection.stop().catch(err => console.log(err));
}
connection = new signalR.HubConnectionBuilder()
.withUrl('myHub')
.build();
connection.on("Heartbeat", serverTime =>
console.log("Server heartbeat: " + serverTime));
connection.on("Send", data =>
_.each(sendHandlers, value => value(data)));
connection.start()
.catch(exception =>
console.log("Error connecting", exception, connection));
}
registerSignalRConnection();
setInterval(() =>
connection.invoke("HeartBeatTock")
.then(() => console.log("Client heatbeat."))
.catch(err => {
registerSignalRConnection();
}), 10 * 1000);
export { addListener };

Resources