How can I test pushsubscriptionchange event-handling code? - web-push

The pushsubscriptionchange event is fired by the browser when the push notification server wants the client to resubscribe. How can I manually trigger this event for testing?

Unfortunately it's not possible to do any end-to-end testing of this feature. The best you can do is fire the event via JS from the service worker:
function triggerSubscriptionChange() {
registration.pushManager.getSubscription().then(subscription => {
if (subscription) {
console.log("subscribed, subscription", subscription);
return subscription.unsubscribe();
}
}).then(() => {
console.log("unsubscribed");
return registration.pushManager.subscribe({
userVisibleOnly: true
});
}).then(subscription => {
console.log("subscribed, subscription", subscription);
self.dispatchEvent(new ExtendableEvent("pushsubscriptionchange"));
});
}

The event is also triggered when the user removes and re-grants the push permission (see https://github.com/w3c/push-api/issues/116).
For example, in Firefox you can click on the site identity icon, in the "Permission" section, select "Block" for "Receive Notifications", then select "Allow".
A pushsubscriptionchange event will be triggered.

Related

How to remove durable subscription using stompit

We are using ActiveMQ 5.16.1 with the stompit client to create a durable subscription in our NodeJS app using the following code snippet:
var connectOptions = {
"host": "",
"port": amqPort,
rejectUnauthorized: false,
checkServerIdentity: () => { },
"connectHeaders": {
"heart-beat": "15000,15000",// hear-beat of 15 seconds
'login': 'admin',
'passcode': 'admin',
'client-id' : "agent-manager"
}
};
var server1 = connectOptions;
server1.host = amqPrimaryHost;
var server2 = connectOptions;
server2.host = amqSecondaryHost;
var amqSubscription;
var subscribeHeaders = {
"destination": "/topic/delivery-reports",
"activemq.subscriptionName": "channel_manager_delivery_reports",
"ack": "client-individual"
};
var connectionManager = new stompit.ConnectFailover([server1,server2], reconnectOptions);
connectionManager.connect(function (error, client, reconnect){
if (error) {
logger.error("Terminal error, gave up reconnecting ", error);
return;
}
client.on("error", function (error) {
if(!client)
reconnect();
});
amqSubscription=client.subscribe(subscribeHeaders, function (error, message,subscription) {
logger.info("going to subscribe")
if (error) {
logger.error("Subscription failed. Going to disconnect", error);
subscription.unsubscribe();
// reconnect();
}
logger.info("subscribed")
});
});
function unsubscribe () {
logger.info("Going to unsub")
amqSubscription.unsubscribe({"activemq.subscriptionName":"channel_manager_delivery_reports"})
};
However, when I call the unsubscribe, it only changes the Subscriber active status to false but does not remove it from the active subscribers' list as shown in the screenshot.
Getting the following exception in stomp.logs.
2021-05-12 05:20:14,826 [0.1:50251#61613] WARN ProtocolConverter - Exception occurred for client agent-manager (tcp://127.0.0.1:50251) processing: UNSUBSCRIBE -> javax.jms.JMSException: Durable consumer is in use
2021-05-12 05:20:14,826 [0.1:50251#61613] DEBUG ProtocolConverter - Exception detail
javax.jms.JMSException: Durable consumer is in use
at org.apache.activemq.broker.region.TopicRegion.removeSubscription(TopicRegion.java:220)
at org.apache.activemq.broker.region.RegionBroker.removeSubscription(RegionBroker.java:457)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.advisory.AdvisoryBroker.removeSubscription(AdvisoryBroker.java:396)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.broker.BrokerFilter.removeSubscription(BrokerFilter.java:119)
at org.apache.activemq.broker.TransportConnection.processRemoveSubscription(TransportConnection.java:419)
at org.apache.activemq.command.RemoveSubscriptionInfo.visit(RemoveSubscriptionInfo.java:81)
at org.apache.activemq.broker.TransportConnection.service(TransportConnection.java:331)
at org.apache.activemq.broker.TransportConnection$1.onCommand(TransportConnection.java:200)
at org.apache.activemq.transport.MutexTransport.onCommand(MutexTransport.java:45)
at org.apache.activemq.transport.AbstractInactivityMonitor.onCommand(AbstractInactivityMonitor.java:301)
at org.apache.activemq.transport.stomp.StompTransportFilter.sendToActiveMQ(StompTransportFilter.java:97)
at org.apache.activemq.transport.stomp.ProtocolConverter.sendToActiveMQ(ProtocolConverter.java:179)
at org.apache.activemq.transport.stomp.ProtocolConverter.onStompUnsubscribe(ProtocolConverter.java:714)
at org.apache.activemq.transport.stomp.ProtocolConverter.onStompCommand(ProtocolConverter.java:251)
at org.apache.activemq.transport.stomp.StompTransportFilter.onCommand(StompTransportFilter.java:85)
at org.apache.activemq.transport.TransportSupport.doConsume(TransportSupport.java:83)
at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:233)
at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:215)
at java.lang.Thread.run(Thread.java:748)
2021-05-12 05:20:14,827 [0.1:50251#61613] TRACE ProtocolConverter - Command that caused the error: UNSUBSCRIBE
activemq.subscriptionName:channel_manager_delivery_reports
receipt:1
id:1
Any suggestion on how to remove the durable subscription properly via stompit.
You first need to disconnect the connection which the durable subscriber is using. This deactivates the subscription and will prevent the JMSException: Durable consumer is in use you're seeing.
Then you need to reconnect using the same client-id header value which you used on your CONNECT frame for the connection used to subscribe.
Then need to pass the activemq.subscriptionName header in the UNSUBSCRIBE frame just like you did for the SUBSCRIBE frame, e.g.:
amqSubscription.unsubscribe({"activemq.subscriptionName": "channel_manager_delivery_reports"})
To be clear, this was resolved awhile back via AMQ-1890. You can see the corresponding source code which checks for the header and removes the subscription. You can also see the unit test which subscribes, disconnects, reconnects, and unsubscribes the durable subscription.
If you still have trouble then it may be worth turning on STOMP trace logging in the broker using these instructions to ensure the UNSUBSCRIBE frame is being received with the expected activemq.subscriptionName header.

How to add subscriber role and publisher role to deadletter for google cloud pubsub using nodeJS?

We've been following https://cloud.google.com/pubsub/docs/dead-letter-topics and nodeJS client to create, update our pubsub topics and subscriptions but after following:
async function createSubscriptionWithDeadLetterPolicy() {
// Creates a new subscription
await pubSubClient.topic(topicName).createSubscription(subscriptionName, {
deadLetterPolicy: {
deadLetterTopic: pubSubClient.topic(deadLetterTopicName).name,
maxDeliveryAttempts: 10,
},
});
console.log(
`Created subscription ${subscriptionName} with dead letter topic ${deadLetterTopicName}.`
);
console.log(
'To process dead letter messages, remember to add a subscription to your dead letter topic.'
);
}
We get this in the dead-letter
This Suggests running the command in CLI for each dead-letter but we don't want to do it manually for each subscription is there a way to do this in nodeJS client itself?
Or doing so for all the subscriptions once and for all even for the new subscriptions that will be created in given project later on.
According to this part of the documentation, you need to grant 2 roles to the PubSub service agent service account. And of course, you can do it by API calls. And, it's not so easy!
In fact, it's not difficult, just boring! Why? Because, you can't only "add" a policy, you set the whole policies. To achieve this:
Get all the existing policies
Add your policy in the existing list
Submit the new list of policies.
You need to do this:
Either globally by setting the policies at the project level. Easier but less secure (break the least privilege principle)
Or on each Dead Letter topic and on each subscription with Dead Letter set up.
You have code example in the Client library doc
EDIT1
If you script the grant access mechanism, you don't care to find it or not: it exists, that's all! Maybe you don't view it on the console, but it exists. Only the pattern is important:
service-<project-number>#gcp-sa-pubsub.iam.gserviceaccount.com
If you are looking for it on the console, it's tricky now! You have to go to Access -> IAM. And then click on the check box on the top rigth corner to display the Google technical accounts
In case anyone needs it, here are the functions that I made up from #guillaume blaquiere's answer:
private async bindPolicyToSubscriber(
subscriptionTopicName: string,
subscriptionName: string,
) {
if (process.env.PROJECT_NUMBER) {
try {
const pubSubTopic = this.getClient().topic(subscriptionTopicName);
const myPolicy = {
bindings: [
{
role: 'roles/pubsub.subscriber',
members: [
`serviceAccount:service-${process.env.PROJECT_NUMBER}#gcp-sa-pubsub.iam.gserviceaccount.com`,
],
},
],
};
await pubSubTopic
.subscription(subscriptionName)
.iam.setPolicy(myPolicy);
} catch (e) {
console.error('Error while binding policy.', e);
}
}
}
private async bindPolicyToDeadLetterTopic(deadLetterTopicName: string) {
if (process.env.PROJECT_NUMBER) {
try {
const pubSubTopic = this.getClient().topic(deadLetterTopicName);
const myPolicy = {
bindings: [
{
role: 'roles/pubsub.publisher',
members: [
`serviceAccount:service-${process.env.PROJECT_NUMBER}#gcp-sa-pubsub.iam.gserviceaccount.com`,
],
},
],
};
await pubSubTopic.iam.setPolicy(myPolicy);
} catch (e) {
console.error('Error while binding policy.', e);
}
}
}

Azure BotFramework Directline polling interval causes events to continuously trigger

I am currently developing a prototype to communicate data between a chatbot and website elements. I am using the Azure Bot Services BotFrameworkAdapter and DirectLine in order to communicate between the two applications.
I am having a problem which I have narrowed down to the 'pollingInterval' property of the DirectLine object. The documentation says:
Clients that poll using HTTP GET should choose a polling interval that matches their intended use.
Service-to-service applications often use a polling interval of 5s or 10s.
Client-facing applications often use a polling interval of 1s, and issue a single additional request shortly after every message that the client sends (to rapidly retrieve a bot's response). This delay can be as short at 300ms but should be tuned based on the bot's speed and transit time. Polling should not be more frequent than once per second for any extended period of time.
To my understanding this is how the DirectLine object receives events from the bot, however I only need the event to trigger once, and not on the polling interval. It seems like there is no way to say "I am finished with this event" and move on. As soon as it is triggered once, it is continuously triggered which is causing issues with the functionality of the application.
I have this BotConnection object that is used to create a DirectLine instance and subscribe event handlers to the received events:
import { DirectLine } from 'botframework-directlinejs';
export default class BotConnection {
constructor() {
//Connect to directline object hosted by the bot
this.connection = new DirectLine({
domain: 'http://localhost:3001/directline',
pollingInterval: 1000,
webSocket: false
})
}
getConnection() {
return this.connection;
}
subscribeEventHandler(name, handle) {
console.log('subscribeEventHandler');
this.connection.activity$
.filter(activity => activity.type === "event" && activity.name === name)
.subscribe(activity => handle(activity));
}
}
I am implementing the botConnection class in my App file like so:
props.botConnection.subscribeEventHandler('changePage', () => console.log('test'));
My Bot file takes a message and sends an event to the page that should be handled once on the client application:
const { ActivityHandler } = require('botbuilder');
class MyBot extends ActivityHandler {
constructor() {
super();
this.onMessage(async (context, next) => {
//Testing directline back-channel functionality
if(context.activity.text === "Change page") {
await context.sendActivity({type: 'event', name: 'changePage', value: '/test'});
}
await next();
}
}
Any help with this issue would be fantastic. I am unsure if there is something supported by Azure or if there is some custom magic that I need to do.
Thanks

How to add Get Started button in the typing bar using bot builder sdk for node.js

I am using bot builder sdk for node.js to create a chatbot. Also connected it to facebook channel. I am using the following code to greet the user:
var bot = new builder.UniversalBot(connector, [
(session, result, next) => {
let text = '';
switch(session.message.address.channelId) {
case 'facebook':
text = 'Hi ' + session.message.user.name + ' !';
break;
default:
text = 'Hi !';
}
session.sendTyping();
session.say(text);
next();
},
(session, say) => {
}
]);
The above code works fine, but I want to add "Get Started" button in the typing bar to invoke the above code. Note that this button appears only once. Please find image of the typing bar below:
Is there a way to achieve this using bot builder sdk for node.js ?
Thanks
Although one can certainly add a button to start any activity with the bot, but that will limit the bots potential to only one customizable channel, i.e. WebChat.
I think there are better 2 alternative ways to get the desired functionality which will work across many channels.
First
I would suggest to add a conversation update event. Code goes in the botbuilder's middleware. Here is a sample code from the docs.
bot.on('conversationUpdate', function (message) {
if (message.membersAdded && message.membersAdded.length > 0) {
// Say hello
var txt = "Send me a Hi";
var reply = new builder.Message()
.address(message.address)
.text(txt);
bot.send(reply);
});
What this will do is make the bot send a message Send me a Hi to the user, if it determines this is a first time visitor. This will give the visitor enough cue to send the bot Hi by typing it. Although he can enter whatever he wants, but this will result in the invocation of the 1st dialog configured which in this case is the will be the dialog which you have posted in question.
Second
You can mark some dialog to be invoked automatically if your bot has never encountered this visitor. Here is the sample code...
var bot = new builder.UniversalBot(connector);
bot.dialog('firstRun', function (session) {
session.userData.firstRun = true;
session.send("Hello...").endDialog();
}).triggerAction({
onFindAction: function (context, callback) {
// Only trigger if we've never seen user before
if (!context.userData.firstRun) {
// Return a score of 1.1 to ensure the first run dialog wins
callback(null, 1.1);
} else {
callback(null, 0.0);
}
}
});
Here we have split the bot creation and dialog registration in 2 steps. And while registering the firstRun dialog, we have provided it the triggerAction that if the visitor is new, then trigger this dialog.
Both of these approaches do not use adding some extra buttons and it is up to the bot either to educate him on sending some message which in turn will start the 1st dialog or directly start some dialog.
For more info on conversationEvent you can refer to this page
I tried the above options, but they didn't seem to be working for facebook messenger. But I found a solution to add the Get Started button into the typing bar of the messenger. For that we need to use the Facebook Graph API and not the bot builder sdk.
https://graph.facebook.com/v2.6/me/messenger_profile?access_token=<PAGE_ACCESS_TOKEN>
{
"get_started":{
"payload":"Get Started"
}
}
The above API call will add the button for you to get the conversation started.
Thanks all for the help!!

Trigger a specific dialog in Bot via Directline API

I'm working on breaking my bot repo into 2 separate repos
A repo to purely handle bot logic
A repo to handle custom chat via directline
Currently , we have a feature where we can trigger the bot to start a specific dialog if its mentioned as a parameter in the URL. So something like
https://foo.com/?param=bar
would trigger the bar dialog
This is the code that handles it
function(userId, conversationId, params, token){
return new Promise((resolve, reject)=>{
var _directlineAddress = {
bot: {"id":config.BOT.ID, "name": config.BOT.HANDLE},
channelId: "directline",
serviceUrl: config.BOT.DIRECTLINE_URL,
useAuth: true,
user:{"id": userId},
"conversation": {"id": conversationId}
}
if(params.options){
var _re = /^\?(\w+)*=(\w+)*/
var _programType = _re.exec(params.options);
if (_programType[1] === "foo") {
var _dialogId = "*:/foo";
}
else {
var _dialogId = "*:/" + _programType[1];
}
} else {
var _dialogId = "*:/";
var _specialParams = {"sessionId":token};
}
bot.beginDialog(_directlineAddress, _dialogId, _specialParams, function(err){
else{
resolve();
}
});
})
};
Since i'm splitting the directline from the bot logic , i will no longer be having access to the bot object. therefore bot.beginDialog would not work here
Is there a way i can trigger the dialog by posting to the Directline API?
No. With Direct Line you will be able to send messages to the bot. I guess that a way to go here will be to define a convention message that you will send via Direct Line and that the bot logic will know that it will have to start a dialog based on it.

Resources