why messaging().sendtodevice is not working sometimes? - javascript

I'm using the following code to send a notification from one device to another using FCM. Everything works fine until before return admin.messaging().sendToDevice(...). The 'Token ID: ' log displays token ID of the receiver, but when I set the variable token_id to the sendToDevice function, the notification is not called, therefore the notification is not sent. Can someone tell me what's wrong?
var firebase = require("firebase-admin");
var serviceAccount = require("./julla-tutorial.json");
console.log("enter in then Firebase Api");
const firebaseToken = [
'e0T6j1AiRjaa7IXweJniJq:APA91bHNznSHSIey08s-C-c3gchci6wepvhP1QxQyYbmZ8LySI3wnu64iW7Q23GhA6VCdc4yodZoCFOgynfAb5C8O8VE81OcSv_LL-K3ET1IKGZ_6h35n-_q5EKFtfJWlzOqZr4IvpiB',
'dNWnSqyCQbufzv1JutNEWr:APA91bFcI9FDyRxHRBEcdw4791X0e-V0k1FjXcSstUA67l94hSojMRCd6LWr2b57azNEt3z_XLwLljMX4u2mc9cZDrAVm55Mw9CHGyue-09KofWnnHNR9XWBibc4T76xOV_DWX7T2RvW',
'cq65rtuaTCKGk5lHk7UabN:APA91bFR3kAArg6lhuBq7ktNuBk7Z9MXXk3PskqhYa8CgNaEl6MX4TQ5lo35d6XhnCQ4fEkCkyZ_j08evxE9Y4oVCRTEdqsrkccCVTE8Di47lfmDR3i1NdoL3re9oLw6F_uNsnvRoQcq'
]
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount)
})
const payload = {
notification: {
title: 'Demo 2345',
body: 'dfghj',
sound: 'default',
color: 'yellow',
android_channel_id: 'default',
channel_id: 'default'
},
data: { id: 'broadcast', channelId: 'default' }
}
const options = {
priority: 'high',
timeToLive: 60 * 60 * 24, // 1 day
};
console.log('------payload---',payload);
console.log('-----TOKEN_Array----',firebaseToken);
console.log('-------options-----',options);
firebase.messaging().sendToDevice(firebaseToken, payload, options).then(function (response) {
console.log('--------response',response);
}) .catch(function (error) {
console.log('-------rejet',reject);
});

It looks like you did not change the code from this tutorial:
https://medium.com/#jullainc/firebase-push-notifications-to-mobile-devices-using-nodejs-7d514e10dd4
you will need to change the 2nd line of code:
var serviceAccount = require("./julla-tutorial.json");
to actually point to your own firebase-push-admin.json file which holds your private keys registering your backend app with the firebase cloud messaging api. you can download this file from the firebase console as mentioned in the above article.
I recommend hiding this file from your git history by adding it to .gitignore so you dont accidentally push your private keys to a public repo.
I will link you another resource in addition to above link which helped me implement firebase push notifications in a nodeJS backend app.
https://izaanjahangir.medium.com/setting-schedule-push-notification-using-node-js-and-mongodb-95f73c00fc2e
https://github.com/izaanjahangir/schedule-push-notification-nodejs
Further I will also link you another repo where I am currently working on a fully functional firebase push notification implementation. Maybe it helps to actually see some example code.
https://gitlab.com/fiehra/plants-backend

Related

Hosting pictures with React and Google drive

I would like to host around 300 photo on Google drive and be able to fetch every photo on my react project, but I don't know how to start like should I need a back-end like node.js? I've seen this video (https://www.youtube.com/watch?v=iajv8y-6n3Q), but he displays only 1 photo.
I wanna display all my images from Google drive on the React and put it on a array like this following JSON
const photos = [
{
src: 'https://source.unsplash.com/2ShvY8Lf6l0/800x599',
width: 4,
height: 3,
year: '2021',
event: 'Hiking',
},
{
src: 'https://source.unsplash.com/Dm-qxdynoEc/800x799',
width: 1,
height: 1,
year: '2021',
event: 'J-On',
},
{
src: 'https://source.unsplash.com/qDkso9nvCg0/600x799',
width: 3,
height: 4,
year: '2021',
event: 'Language Exchange',
},
and so on...
You can achieve your goal in two ways:
Get the files link by manually copying the link from google drive
Fetch the data by using Next.js app and googleapis.
1. Get the files link by manually copying the link from google drive:
Open the file on google drive
Right click at the image file
Click Get Link and a pop up will open
At General Access select Anyone with the link and make sure the role is Viewer
Click Copy Link and done.
Then paste it at any text editor, and you will get the link like this:
https://drive.google.com/file/d/1WS45ByTh_e8QNJTeFMGI8TYpE4OYS2U4/view?usp=sharing
Remove the file/d and replace it with uc?export=view&id= and remove the /view?usp=sharing completely and you will get something like:
https://drive.google.com/uc?export=view&id=1WS45ByTh_e8QNJTeFMGI8TYpE4OYS2U4
And put the image information in an array:
const images = [
{
name: "Doctor Strange in the Multiverse of Madness",
src: "https://drive.google.com/uc?export=view&id=1WS45ByTh_e8QNJTeFMGI8TYpE4OYS2U4"
}
]
Please do the same with the other files, and your images constant finally looks like this:
const images = [
{
name: "Doctor Strange in the Multiverse of Madness",
src:
"https://drive.google.com/uc?export=view&id=1WS45ByTh_e8QNJTeFMGI8TYpE4OYS2U4",
},
{
name: "Minions: The Rise of Gru",
src:
"https://drive.google.com/uc?export=view&id=1eUvlVjwp2brHnyD-cWN0x-10tuS_wXfU",
},
...
,
{
name: "Fantastic Beasts: The Secrets of Dumbledore",
src:
"https://drive.google.com/uc?export=view&id=1vU64yCMdu_ns4xPP-VMYoiJPKNietxRc",
},
];
Now, it is time to render the images in our React Application by using javascript's map method:
export default function App() {
return (
<div>
<div>My Google Drive Images</div>
<ul>
{images.map((image, index) => {
return (
<li key={index}>
<h1>{image.name}</h1>
<img src={image.src} alt={image.name} />
</li>
);
})}
</ul>
</div>
);
}
2. Fetch the data by using Next.js app and googleapis.
In case we have many photos, it is better to use data fetching. Here we use Next.js app and googleapis library.
There are 3 pieces of information that we need to provide in order to be able to fetch data from https://www.googleapis.com/drive/v2 server which are:
CLIENT_ID
CLIENT_SECRET
REFRESH_TOKEN
For CLIENT_ID & CLIENT_SECRET are OAuth 2.0 Client information that can be obtained from Google Developers Console. You can read the details here. And for the REFRESH_TOKEN can be obtained from OAuth 2.0 Playground and the steps are as follows:
Go to https://developers.google.com/oauthplayground
Click the OAuth 2.0 Configuration button
Click Use your own OAuth credentials checkbox
Fill the Client ID & Client Secret input with the OAuth 2.0 CLIENT_ID & CLIENT_SECRET that we get from Google Developers Console.
Close the configuration popup by clicking the close link button.
Find and click the Drive API v3 at Step 1 Select & authorizes APIs.
Select https://www.googleapis.com/auth/drive to enable the Authorize APIs button.
Next we need to select which information that can be accessed by using the token that will be created by clicking the Authorize APIs button.
Then we will be directed to the Sign in with google page. Select the account that we used to generate the CLIENT_ID & CLIENT_SECRET.
If the Google hasn’t verified this app page appears, just click Advance and click Go to ...(unsafe) and select the access types that will be granted to our Next.js app then click Continue and you will be redirected to OAuth 2.0 Playground again.
Find the Exchange authorization code for tokens button at Step 2 Exchange authorization code for tokens, and click it to get the Refresh token and Access token
To make sure that we have generated the tokens correctly, go to https://www.googleapis.com/drive/v2/files?access_token={ACCESS_TOKEN} in your browser. Our configuration is successful if there is a raw JSON page that shows our drive items.
Now, we are ready for coding. We will use our NextJs API to communicate with googleapis server and serve the data to be consumed by our app.
Open your Nextjs app, create a /pages/api/drive-files.ts file, and import googleapis library (here we use Typescript).
import type { NextApiRequest, NextApiResponse } from "next";
import { google } from "googleapis";
...
Create types:
type DriveFile = {
kind: string;
id: string;
name: string;
mimeType: "image/jpeg";
};
type Data = {
files: DriveFile[];
};
Add the Data type at the NextApiResponse and the handler will look like this:
export default async function handler(
_req: NextApiRequest,
res: NextApiResponse<Data>
) {
...
}
Next, create oauth2Client that use CLIENT_ID and CLIENT_SECRET credentials and we use https://developers.google.com/oauthplayground as the redirect uri, setCredentials using REFRESH_TOKEN, and return the google.drive response as JSON;
# /pages/api/drive-files.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { google } from "googleapis";
export type DriveFile = {
kind: string;
id: string;
name: string;
mimeType: "image/jpeg";
};
export type Data = {
files: DriveFile[];
};
const CLIENT_ID =
"777777777777-xxxxddhsl77d7o77777xxxx0v777xx7.apps.googleusercontent.com";
const CLIENT_SECRET = "XXXXXX-777XXXX_-7-hexxxxheAzW_7mr_7_";
const REDIRECT_URI = "https://developers.google.com/oauthplayground";
export default async function handler(
_req: NextApiRequest,
res: NextApiResponse<Data>
) {
const oauth2Client = new google.auth.OAuth2(
CLIENT_ID,
CLIENT_SECRET,
REDIRECT_URI
);
oauth2Client.setCredentials({
refresh_token:
"1//04tORk-dUiDiJCgYIARAAGAQSNwF-L9Ir1hzh14oOk6gQWMOafDZGvLuka578PwwmZB3UMbB2a0VdcjAbRjtelFoDU92ob_Ws50I",
});
const drive = google.drive({
version: "v3",
auth: oauth2Client,
params: {
q: `mimeType = 'image/jpeg'`,
},
});
const response = await drive.files.list();
res.status(200).json({ files: response.data.files as DriveFile[] });
}
And when you access http://localhost:3000/api/drive-files from the browser, you will get:
{
"files":[
{
kind: 'drive#file',
id: '1eUvlVjwp2brHnyD-cWN0x-10tuS_wXfU',
name: 'Minions: The Rise of Gru.jpg',
mimeType: 'image/jpeg'
},
{
kind: 'drive#file',
id: '1vU64yCMdu_ns4xPP-VMYoiJPKNietxRc',
name: 'Fantastic Beasts: The Secr.jpg',
mimeType: 'image/jpeg'
},
{
kind: 'drive#file',
id: '1WS45ByTh_e8QNJTeFMGI8TYpE4OYS2U4',
name: 'Doctor Strange in the M.jpg',
mimeType: 'image/jpeg'
}
]
}
Since we will use NextJs Image component, don't forget to add domain drive.google.com at next.config.js:
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
images: {
domains: ["drive.google.com"],
},
};
module.exports = nextConfig;
Finally, you can render the images as follows:
import { useEffect, useState } from "react";
import type { NextPage } from "next";
import axios from "axios";
import { DriveFile } from "./api/drive-files";
import Image from "next/image";
const DriveFiles: NextPage = () => {
const [files, setFiles] = useState<DriveFile[]>();
useEffect(() => {
axios
.get("/api/drive-files")
.then((res) => {
setFiles(res.data.files);
})
.catch((err) => {
console.log(err);
});
}, []);
useEffect(() => {
console.log(files);
}, [files]);
return (
<>
<h1>Google Drive Files</h1>
{files &&
files.map((file, i) => {
return (
<div key={i}>
<p>{file.name}</p>
<Image
width={200}
height={300}
src={`https://drive.google.com/uc?export=view&id=${file.id}`}
alt={file.name}
/>
</div>
);
})}
</>
);
};
export default DriveFiles;

MS graph API: 400 AuthenticationError with "/me/onlineMeetings" request

I am trying to create an online meeting and recover its URL like explained here in the docs, but when the request is run I get this error:
{
"statusCode": 400,
"code": "AuthenticationError",
"message": "Error authenticating with resource",
"requestId": "652ea3be-6a97-47e8-bfc6-3d7d1d51d425",
"date": "2020-09-01T12:53:41.000Z",
"body": "{
"code":"AuthenticationError",
"message":"Error authenticating with resource",
"innerError":{
"date":"2020-09-01T13:53:41",
"request-id":"652ea3be-6a97-47e8-bfc6-3d7d1d51d425"
}
}"
}
I tried also the get started projet for JS and it's working fine so I can't spot the problem.
here is what I used:
const msalConfig = {
auth: {
clientId: 'my_app_id',
redirectUri: 'http://localhost:8080'
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false,
forceRefresh: false
}
};
const loginRequest = { scopes: [
'openid',
'profile',
'user.read',
'calendars.read',
'User.Read.All',
'User.Export.All'
]
}
const options = new MicrosoftGraph.MSALAuthenticationProviderOptions([
'user.read',
'calendars.read',
'OnlineMeetings.ReadWrite'
]);
const onlineMeeting = {
startDateTime:"2020-09-01T16:00:34.2444915-07:00",
endDateTime:"2020-09-01T16:30:34.2464912-07:00",
subject:"test meeting"
};
const authProvider = new MicrosoftGraph.ImplicitMSALAuthenticationProvider(msalClient, options);
// Initialize the Graph client
const graphClient = MicrosoftGraph.Client.initWithMiddleware({authProvider});
// then I call this inside an async function
let events = await graphClient.api('/users/my_UserPrincipalName/onlineMeetings').post(onlineMeeting);
//let events = await graphClient.api('/me/onlineMeetings').post(onlineMeeting);
// I tried with both calls and none of them worked
and here are the permissions on azure active directory:
So any ideas on how to solve this ?
thanks
You didn't provide a correct access token.
Since Create onlineMeeting only supports Delegated (work or school account) permission type, you need to get the access token with Auth code flow or Implicit flow.
The started project for JS is using Implicit flow. So you can use Implicit flow to get the access token.
Here is the example in Postman:
The Auth URL above is https://login.microsoftonline.com/{your tenant}/oauth2/v2.0/authorize.
I figured out how to make it work in my code:
let's call my user, which I used all this time, user "A", all I did is that I simply created another user "B" in Azure Active Directory and then logging in with this new user "B" in the login screen instead of the admin user "A" that I used before..... and now it's working.
But this does not explain the issue, so if anyone can explain the difference or why it didn't work with the first account, that would be very helpful.

How to update clients when database is edited outside of feathersjs app

I have two server-side applications editing the same database.
One server is a feathersjs app and another is a simple nodejs app. A frontend app connects to the feathersjs app via feathersjs client.
How, when the nodejs app edits the database, can I update clients connected to the feathersjs app? As currently any changes made outside the featherjs app aren't reflected on the feathersjs clients.
Can I trigger the patched event somehow and force the clients to pull down the updated data?
if you are using mongodb with WiredTiger storageEngine you can use the collection.watch() function and add a monitor in your feathers app something like this
//src/services/monitor.js
module.exports = function(app){
//get mongo client
const mongoClient = app.get('mongoClient');
//connect db
mongoClient.then(db => {
//collection to watch
const collection = db.collection('your_collection_name')
//watch collection
const changeStream = collection.watch({ fullDocument: 'updateLookup' });
//on some data changed
changeStream.on('change', data => {
console.log ( 'something is changed in your collection' , data )
//your service.emit
});
})
}
Then I added this simple monitor in the /src/services/index.js (maybe not the right way but it works)
//src/services/index.js
...
const monitor = require('./monitor.js');
module.exports = function (app) {
...
app.configure(monitor);
...
};
Data returned on every change on the collection
{ _id:
{ _data:
'825C7F03230000001C29295A100490DEF2C65812410FABF0DE46F9C89D7246645F696400645C1AC097B189CBD5D4A24D330004' },
operationType: 'replace',
clusterTime:
Timestamp { _bsontype: 'Timestamp', low_: 28, high_: 1551827747 },
fullDocument:
{ _id: 5c1ac097b189cbd5d4a24d33,
id: '12',
language: 'it-IT',
category: 'some data',
slug: '',
description: 'some data',
src:'',
color: 'card',
status: true,
home: true,
order_int: '3',
visual: 'card' },
ns: { db: 'mydb', coll: 'mycollection' },
documentKey: { _id: 5c1ac097b189cbd5d4a24d33 } }
More info here https://docs.mongodb.com/manual/reference/method/db.collection.watch/
As you pointed out, only changes made through the Feathers API will be reflected but on the server you can always emit the event you need via service.emit:
dbConnection.on('someDatabaseUpdate', data => {
app.service('messages').emit('patched', data);
app.service('messages').emit('updated', data);
});
Things to note here (also discussed in this issue):
There will be no user or any other information about the method call
data will not be run through any service hooks

How to login to gmail account and read a mail in protractor not using the browser

I have a scenario which I need to login to a gmail account and read the content or the message. Then need to get a URL from that message. I can do this using a browser in protractor. But the issue is that gmail account enabled the 2FA. I have achieved this using the core Selenium which has jar files to log in to the gmail account using IMAP protocol.
Can someone please give me a good solution?
You can read emails from Gmail inside protractor tests using mail-listener2 npm package. Check the below example code.
mailListener.ts
const MailListenerClient = require("mail-listener2");
const cheerio = require('cheerio');
const simpleParser = require('mailparser').simpleParser;
export class MailListener {
public mailListener:any;
constructor() {
this.mailListener = new MailListenerClient({
username: "username#gmail.com",
password: "password",
host: "imap.gmail.com",
port: 993,
tls: true,
mailbox: "INBOX",
searchFilter: ["UNSEEN", ["FROM", "fromemail#gmail.com"],["SUBJECT","subject of the email"]],
/*it will search for are "unseen" mail send from "fromemail#gmail.com" with subject "fromemail#gmail.com"*/
connTimeout: 10000,
authTimeout: 5000,
markSeen: true,
mailParserOptions: {streamAttachments: true}, // options to be passed to mailParser lib.
attachments: true, // download attachments as they are encountered to the project directory
attachmentOptions: {directory: "attachments/"},
debug : console.log
});
}
init() {
this.mailListener.start();
}
close() {
this.mailListener.stop();
}
getLinkFromEmail() {
var self = this;
return new Promise((resolve, reject) => {
self.mailListener.on("mail", function (mail) {
/*simpleParser is used to convert string to HTML format*/
simpleParser(mail.eml).then(function (parsedEmail) {
var html = parsedEmail.html;
/* cheerio is used to write query on parsed HTML content
* refer https://www.npmjs.com/package/cheerio
*/
resolve(cheerio.load(html)("a").attr("href"));
});
});
self.mailListener.on("error", function (err) {
reject(err);
});
});
}
}
test.ts
import {MailListener} from "mailListner";
describe("Read email from gmail using imap", function () {
let mailListener = new MailListener();
beforeAll(function(){
mailListener.init();
});
afterAll(function(){
mailListener.close();
})
it("Test email recieved",function(){
let urlFromEmail = mailListener.getLinkFromEmail();
/*Perform some action on UI that triggers email.(Just for example im doing it)*/
element(by.id("email")).sendKeys("email#gmail.com");
element(by.buttonText("Send Email")).click();
expect(urlFromEmail).toEqual("some link");
})
});
I have written the code in typescript and hope you can rewrite the same in javascript. Let me know if this is clear or do I need to add more details to the code.
One of the best practice is to use Gmail API. Take a look at official documentation

How to use servicebus topic sessions in azure functionapp using javascript

I have an Azure Functionapp that processes some data and pushes that data into an Azure servicebus topic.
I require sessions to be enabled on my servicebus topic subscription. I cannot seem to find a way to set the session id when using the javascript functionapp API.
Here is a modified extract from my function app:
module.exports = function (context, streamInput) {
context.bindings.outputSbMsg = [];
context.bindings.logMessage = [];
function push(response) {
let message = {
body: CrowdSourceDatum.encode(response).finish()
, customProperties: {
protoType: manifest.Type
, version: manifest.Version
, id: functionId
, rootType: manifest.RootType
}
, brokerProperties: {
SessionId: "1"
}
context.bindings.outputSbMsg.push(message);
}
.......... some magic happens here.
push(crowdSourceDatum);
context.done();
}
But the sessionId does not seem to get set at all. Any idea on how its possible to enable this?
I tested sessionid on my function, I can set the session id property of a message and view it in Service Bus explorer. Here is my sample code.
var connectionString = 'servicebus_connectionstring';
var serviceBusService = azure.createServiceBusService(connectionString);
var message = {
body: '',
customProperties:
{
messagenumber: 0
},
brokerProperties:
{
SessionId: "1"
}
};
message.body= 'This is Message #101';
serviceBusService.sendTopicMessage('testtopic', message, function(error)
{
if (error)
{
console.log(error);
}
});
Here is the test result.
Please make sure you have enabled the portioning and sessions when you created the topic and the subscription.

Categories

Resources