Communicate notification data from messaging.setBackgroundMessageHandler to webpage - firebase

I am trying to communicate data received by the service worker back to webpage.
On the webpage 'navigator.serviceWorker.controller' is null. The sevice worker has self.client as empty.
Any samples or directions will help

What you can do is get a list of window clients which will return a list of the tabs for your origin and then post a message to each window client. (This code would be in the setBackgroundMessageHandler() ):
const promiseChain = clients.matchAll({
type: 'window',
includeUncontrolled: true
})
.then((windowClients) => {
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
windowClient.postMessage(data);
}
})
.then(() => {
return registration.showNotification('my notification title');
});
return promiseChain;
Then to receive the message in the page, add a listener like so:
navigator.serviceWorker.addEventListener('message', function(event) {
console.log('Received a message from service worker: ', event.data);
});

Related

Cypress reutrns 302 instead of 200

I was trying to test sign-in page of our app. I am using cypress to test Vuejs frontend works with AspNet Api. When I click on the signin button on chrome it makes following requests and visits the homepage "localhost:44389"
first request from Chrome
second request from Chrome
if I want to simulate it on cypress it sends same request but get 302 from second request instead of 200.
first request from Cypress
second request from Cypress
Can someone help me to find out the problem?
Cypress.Commands.add('IdentityServerAPILogin', (email, password) => {
console.log("SERVER_URL is called. Server: " + Cypress.env('SERVER_URL'));
cy.request({
method: 'GET',
url: Cypress.env('SERVER_URL'),
failOnStatusCode: false,
headers: {
'Cookie': '...coookies...'
}
})
.then(response => {
if (response.status == 401){
console.log ("Check for NOnce");
console.dir(response, { depth: null });
const requestURL = response.allRequestResponses[1]["Request URL"]
console.dir(requestURL, { depth: null })
//const signInPage = (response.redirects[0]).replace('302: ', '');
const signInPage = (response.redirects[1]).replace('302: ', '');
console.log("signInPage:" + signInPage);
const nOnceObj = response.allRequestResponses[0]["Response Headers"];
console.dir(nOnceObj, { depth: null });
const nOnce = nOnceObj["set-cookie"][0];
console.log("Nonce:" + nOnce);
cy.visit({
method: 'GET',
url: signInPage,
failOnStatusCode: false,
headers: {
//'Cookie': nOnce
}
})
// TODO: Create all needed tests later to test sign in
cy.get('#username').type(email)
cy.get('#password').type(password)
// TODO: Waiting for to click signIn button. When I call the click() method I get infinite loop!!
cy.get('.ping-button')
// .click()
// .then((response_login) => {
// console.log("Status REsponse_Login: "+ response_login);
// console.dir(response_login, { depth: null });
// if (response_login.status == 401){
// cy.visit(Cypress.env('SERVER_URL'))
// }
// })
}else
cy.visit(Cypress.env('SERVER_URL'))
})
console.log("vorbei");
});
Just figured out Cypress is not able to get Cookies from .../signin-oidc, because there is an error as in the photo below
Asking kindly for a solution. I am not allowed to make changes on authorization service. Looking for a possibility around cypress.

Communicating with an external service: internal server error 500

I'm trying to make a GET request to an HTTPS service ( https://broker.bronos.net ). This service is an API that communicates with a client on my LAN. I can't get it to work via functions.https.get(URL, (s,ss) => {});
Please help -- I'm very new to web development, let alone google actions.
I'm using the apiai-starter-app as the base, which functions perfectly fine until I add the line above which returns internal server error 500.
Note: I've tried before adding billing to the project and after as well. Neither work.
Edit:
using this
const https = require('https');
https.get('https://broker.bronos.net/v1/CLIENT_ID/ROOM_NAME/ACTION/PARAM', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
if (requestSource === googleAssistantRequest) {
sendGoogleResponse(JSON.parse(data).explanation); // Send simple response to user
} else {
sendResponse(JSON.parse(data).explanation); // Send simple response to user
}
});
}).on("error", (err) => {
if (requestSource === googleAssistantRequest) {
sendResponse("Error: " + err.message); // Send simple response to user
} else {
sendResponse("Error: " + err.message); // Send simple response to user
}
});
Firebase's functions have limited access to external APIs on the free tier. By upgrading to Blaze or Flame plans you will be able to make external API calls.
Enabling Firebase Blaze plan + the following code worked
const https = require('https');
https.get('https://broker.bronos.net/v1/CLIENT_ID/Living%20Room/volume/20', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
if (requestSource === googleAssistantRequest) {
sendGoogleResponse(JSON.parse(data).explanation); // Send simple response to user
} else {
sendResponse(JSON.parse(data).explanation); // Send simple response to user
}
});
}).on("error", (err) => {
if (requestSource === googleAssistantRequest) {
sendResponse("Error: " + err.message); // Send simple response to user
} else {
sendResponse("Error: " + err.message); // Send simple response to user
}
});

Poll HTTP endpoint in Meteor

I am trying to get some info from an API and save this info into a mongodb collection and finally display this information reactively. When the page is loaded it does it perfectly. If I make a manual update in mongo the view changes instantly as well. But after the first load, the page doesn't make new requests anymore, so the external data is no longer refreshed.
I understand that meteor looks for internal data changes, but I didn't find anything to look for external changes, or to make the requests in a loop.
<template name="scanner">
{{#each scan}}
<img src="img/antenna.png" height="25" width="25" style={{position_scanner x y}}
id="{{addr}}" title="{{tip_text addr name}}" >
{{/each}}
</template>
Template.scanner.helpers({
scan: function() {
Meteor.call("get_scanners");
return Scanners.find({});
}
});
Meteor.methods({
get_scanners: function (){
var url = "http://192.168.60.154:5008/api-ot/scanners";
try {
var result = HTTP.get(url);
var statusCode = result.statusCode;
var data = result.data.scanners_list;
for (var x in data) {
var scanner = data[x];
Scanners.update({ addr: scanner.addr }, scanner, { upsert: true });
}
} catch (e) {
console.log("Cannot get scanner data");
}
}
});
Since you want your external call to happen every 10 seconds, you can just do it in Meteor.startup() using Meteor.setInterval() to schedule it. You are updating the Scanners collection which can be sent to the client via publish & subscribe, no Meteor.call() necessary!
Meteor.startup(() => {
Meteor.setInterval(() => {
const = "http://192.168.60.154:5008/api-ot/scanners";
try {
const data = HTTP.get(url).data.scanners_list;
data.forEach(scanner => {
Scanners.update({ addr: scanner.addr }, scanner, { upsert: true });
});
} catch (e) {
console.log("Cannot get scanner data");
}
},10000); // run every 10 seconds == 10000 msec
});

ServiceWorker WindowClient.navigate promise rejected

I'm using Firebase Cloud Messaging + Service worker to handle background push notifications.
When the notification (which contains some data + a URL) is clicked, I want to either:
Focus the window if it's already on the desired URL
Navigate to the URL and focus it if there is already an active tab open
Open a new window to the URL if neither of the above conditions are met
Points 1 and 3 work with the below SW code.
For some reason point #2 isn't working. The client.navigate() promise is being rejected with:
Uncaught (in promise) TypeError: Cannot navigate to URL: http://localhost:4200/tasks/-KMcCHZdQ2YKCgTA4ddd
I thought it might be due to a lack of https, but from my reading it appears as though localhost is whitelisted while developing with SW.
firebase-messaging-sw.js:
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.3/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': 'XXXX'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(payload => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
let notificationData = JSON.parse(payload.data.notification);
const notificationOptions = {
body: notificationData.body,
data: {
clickUrl: notificationData.clickUrl
}
};
return self.registration.showNotification(notificationData.title,
notificationOptions);
});
self.addEventListener('notificationclick', event => {
console.log('[firebase-messaging-sw.js] Notification OnClick: ', event);
// Android doesn’t close the notification when you click on it
// See: http://crbug.com/463146
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.notification.close();
let validUrls = /localhost:4200/;
let newUrl = event.notification.data.clickUrl || '';
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
event.waitUntil(
clients.matchAll({
includeUncontrolled: true,
type: 'window'
})
.then(windowClients => {
for (let i = 0; i < windowClients.length; i++) {
let client = windowClients[i];
if (validUrls.test(client.url) && 'focus' in client) {
if (endsWith(client.url, newUrl)) {
console.log('URL already open, focusing.');
return client.focus();
} else {
console.log('Navigate to URL and focus', client.url, newUrl);
return client.navigate(newUrl).then(client => client.focus());
}
}
}
if (clients.openWindow) {
console.log('Opening new window', newUrl);
return clients.openWindow(newUrl);
}
})
);
});
The vast majority of my SW code is taken from:
https://gist.github.com/vibgy/0c5f51a8c5756a5c408da214da5aa7b0
I'd recommend leaving out includeUncontrolled: true from your clients.matchAll().
The WindowClient that you're acting on might not have the current service worker as its active service worker. As per item 4 in the specification for WindowClient.navigate():
If the context object’s associated service worker client’s active
service worker is not the context object’s relevant global object’s
service worker, return a promise rejected with a TypeError.
If you can reproduce the issue when you're sure the client is currently controlled by the service worker, then there might be something else going on, but that's what I'd try as a first step.
This worked for me:
1- create an observable and make sure not to call the messaging API before it resolves.
2- register the service worker yourself, and check first if its already registered
3- call event.waitUntil(clients.claim()); in your service worker
private isMessagingInitialized$: Subject<void>;
constructor(private firebaseApp: firebase.app.App) {
navigator.serviceWorker.getRegistration('/').then(registration => {
if (registration) {
// optionally update your service worker to the latest firebase-messaging-sw.js
registration.update().then(() => {
firebase.messaging(this.firebaseApp).useServiceWorker(registration);
this.isMessagingInitialized$.next();
});
}
else {
navigator.serviceWorker.register('firebase-messaging-sw.js', { scope:'/'}).then(
registration => {
firebase.messaging(this.firebaseApp).useServiceWorker(registration);
this.isMessagingInitialized$.next();
}
);
}
});
this.isMessagingInitialized$.subscribe(
() => {
firebase.messaging(this.firebaseApp).usePublicVapidKey('Your public api key');
firebase.messaging(this.firebaseApp).onTokenRefresh(() => {
this.getToken().subscribe((token: string) => {
})
});
firebase.messaging(this.firebaseApp).onMessage((payload: any) => {
});
}
);
}
firebase-messaging-sw.js
self.addEventListener('notificationclick', function (event) {
event.notification.close();
switch (event.action) {
case 'close': {
break;
}
default: {
event.waitUntil(clients.claim());// this
event.waitUntil(clients.matchAll({
includeUncontrolled: true,
type: "window"
}).then(function (clientList) {
...
clientList[i].navigate('you url');
...
}
}
}
}

pushPlugin notification on the server and front-en

does this get triggered again from the server containing a message?
"ecb":"window.onNotificationGCM"
I have this set up on the server
device_tokens = [], //create array for storing device tokens
retry_times = 4, //the number of times to retry sending the message if it failed
sender = new gcm.Sender('AIzaSyDpA0b2smrKyDUSaP0Cmz9hz4cQ19Rxn7U'), //create a new sender
message = new gcm.Message(); //create a new message
message.addData('title', 'Open Circles');
message.addData('message', req.query.message);
message.addData('sound', 'notification');
message.collapseKey = 'testing'; //grouping messages
message.delayWhileIdle = true; //delay sending while receiving device is offline
message.timeToLive = 3; //the number of seconds to keep the message on the server if the device is offline
device_tokens.push(val.deviceToken);
sender.send(message, device_tokens, retry_times, function(result){
console.log(result);
console.log('push sent to: ' + val.deviceToken);
});
So what I want to know is, once a server call is made will it trigger the notification on the front. What am I missing about this system?
case 'message':
// if this flag is set, this notification happened while we were in the foreground.
// you might want to play a sound to get the user's attention, throw up a dialog, etc.
if (event.foreground) {
console.log('INLINE NOTIFICATION');
var my_media = new Media("/android_asset/www/" + event.soundname);
my_media.play();
} else {
if (event.coldstart) {
console.log('COLDSTART NOTIFICATION');
} else {
console.log('BACKGROUND NOTIFICATION');
}
}
navigator.notification.alert(event.payload.message);
console.log('MESSAGE -> MSG: ' + event.payload.message);
//Only works for GCM
console.log('MESSAGE -> MSGCNT: ' + event.payload.msgcnt);
//Only works on Amazon Fire OS
console.log('MESSAGE -> TIME: ' + event.payload.timeStamp);
break;
case 'error':
console.log('ERROR -> MSG:' + event.msg);
break;
default:
console.log('EVENT -> Unknown, an event was received and we do not know what it is');
break;
}
};
return {
register: function () {
var q = $q.defer();
if(ionic.Platform.isAndroid()){
pushNotification.register(
successHandler,
errorHandler,
{
"senderID":"346007849782",
"ecb":"window.onNotificationGCM"
}
);
}else{
pushNotification.register(
tokenHandler,
errorHandler,
{
"badge":"true",
"sound":"true",
"alert":"true",
"ecb":"window.onNotificationAPN"
}
);
}
return q.promise;
}
}
update. Eventually my server spit back this: TypeError: Cannot read property 'processIncomingMessage' of undefined
It seems my google ID was not working. I created a new one and now it's sending push requests.

Resources