I'm trying to retrieve value from firebase database which is RTDB
it doesnt send any error or output any error as i know of so im really confused as to why.
it worked fine a minute ago.
below is my code to retrieve value from table and send through telegram
import * as functions from "firebase-functions";
import * as express from "express";
import * as cors from "cors";
import * as admin from "firebase-admin";
admin.initializeApp();
const bot = express();
bot.use(cors({origin: true}));
bot.post("/", async function(req, res) {
if (
req.body &&
req.body.message &&
req.body.message.chat &&
req.body.message.chat.id &&
req.body.message.from &&
req.body.message.from.first_name
) {
const chatId = req.body.message.chat.id;
const firstName = req.body.message.from.first_name;
const receivedMessage = req.body.message.text;
// Check if the received message is the "/start" command
if (receivedMessage === "/start") {
// Define your RTDB Reference to point to the "Sensor MQ7" parent node
const rtdbReference = admin.database().ref("Sensor MQ7");
// Read the latest unknown child node of "Sensor MQ7"
const wildcardNodeSnapshot = await rtdbReference
.limitToLast(1)
.once("child_added");
// Read the known child nodes of the unknown child node
const coConcentrationSnapshot = wildcardNodeSnapshot.child("MQ7");
const latSnapshot = wildcardNodeSnapshot.child("latitude");
const longSnapshot = wildcardNodeSnapshot.child("longitude");
const timeSnapshot = wildcardNodeSnapshot.child("time");
// Get the values of the known child nodes
const coConcentration = coConcentrationSnapshot.val();
const time = timeSnapshot.val();
const latitude = latSnapshot.val();
const longtitude = longSnapshot.val();
// Check if the known child node value is above 100
if (coConcentration > 100) {
// Return the response with a button interface
return res.status(200).send({
method: "sendMessage",
chatId,
text: `${firstName} is in danger! Carbon Monoxide Concentration is too high: ${coConcentration}. Last known location is at (${latitude},${longtitude}) approximately ${time} o clock.`,
reply_markup: {
inline_keyboard: [
[
{
text: `Call ${firstName}`,
callback_data: "call_user",
},
],
],
},
});
}
}
} else {
// Return an error message if the request body is missing the required information
return res.status(400).send({
status: "An error occurred. The request body is missing the required information.",
});
}
// Return an empty response
return res.status(200).send({});
});
export const router = functions.https.onRequest(bot);
expected output should be like this: output
its strange because when i do /start it doesnt even send a blank reply
i was supposed to output the database value
Related
I have created the following user schema, including two methods:
getSnapshot()
getLastTweetId()
user.js
const mongoose = require('mongoose')
const getLastTweetId = require('../utilities/getLastTweetId')
const getFollowers = require('../utilities/getFollowers')
const userSchema = new mongoose.Schema({
twitterId: {
type: String,
required: true
},
screenName: {
type: String
},
snapshots: {
type: [snapshotSchema],
default: []
},
createdAt: {
type: Date
},
})
userSchema.method('getSnapshot', async function () {
const { user, snapshot } = await getFollowers({user: this})
await user.save()
return snapshot
})
userSchema.method('getLastTweetId', async function () {
const tweetId = await getLastTweetId({user: this})
return tweetId
})
const User = mongoose.model('User', userSchema)
module.exports = User
When I define a user instance in passport.js, I can call getSnapshot() on user with no problems. (see below)
passport.js
const passport = require('passport')
const mongoose = require('mongoose')
const needle = require('needle')
const { DateTime } = require('luxon')
const User = mongoose.model('User')
// Setup Twitter Strategy
passport.use(new TwitterStrategy({
consumerKey: process.env.TWITTER_CONSUMER_API_KEY,
consumerSecret: process.env.TWITTER_CONSUMER_API_SECRET_KEY,
callbackURL: process.env.CALLBACK_URL,
proxy: trustProxy
},
async (token, tokenSecret, profile, cb) => {
const twitterId = profile.id
const screenName = profile.screen_name
const existingUser = await User.findOne({ twitterId })
if (existingUser) {
// Track if this is a new login from an existing user
if (existingUser.screenName !== screenName) {
existingUser.screenName = screenName
await existingUser.save()
}
// we already have a record with the given profile ID
cb(undefined, existingUser)
} else {
// we don't have a user record with this ID, make a new record
const user = await new User ({
twitterId ,
screenName,
}).save()
**user.getSnapshot()**
cb(undefined, user)
}
}
)
However, when I call getLastTweetId() on a user instance in tweet.js, I receive the following error in my terminal:
TypeError: user.getLastTweetId is not a function
Then my app crashes.
tweets.js
const express = require('express')
const mongoose = require('mongoose')
const User = mongoose.model('User')
const Tweet = mongoose.model('Tweet')
const { DateTime } = require('luxon')
const auth = require('../middleware/auth')
const requestTweets = require('../utilities/requestTweets')
const router = new express.Router()
const getRecentTweets = async (req, res) => {
const twitterId = req.user.twitterId
const user = await User.find({twitterId})
*const sinceId = user.getLastTweetId()*
let params = {
'start_time': `${DateTime.now().plus({ month: -2 }).toISO({ includeOffset: false })}Z`,
'end_time': `${DateTime.now().toISO({ includeOffset: false })}Z`,
'max_results': 100,
'tweet.fields': "created_at,entities"
}
if (sinceId) {
params.since_id = sinceId
}
let options = {
headers: {
'Authorization': `Bearer ${process.env.TWITTER_BEARER_TOKEN}`
}
}
const content = await requestTweets(twitterId, params, options)
const data = content.data
const tweets = data.map((tweet) => (
new Tweet({
twitterId,
tweetId: tweet.id,
text: tweet.text,
})
))
tweets.forEach(async (tweet) => await tweet.save())
}
// Get all tweets of one user either since last retrieved tweet or for specified month
router.get('/tweets/user/recent', auth, getRecentTweets)
module.exports = router
I would really appreciate some support to figure out what is going on here.
Thank you for bearing with me!
My first guess was that the user instance is not created properly in tweets.js, but then I verified via log messages that the user instance is what I expect it to be in both passport.js as well as tweets.js
My second guess was that the problem is that the user instance in the database was created before I added the new method to the schema, but deleting and reinstantiating the entire collection in the db changed nothing.
Next I went about checking if the issue is related to instantiating the schema itself or just importing it and it seems to be the latter, since when I call getLastTweetId in passport.js it also works, when I call getSnapshot() in tweets.js it also fails.
This is where I'm stuck, because as far as I can tell, I am requiring the User model exactly the same way in both files.
Even when I print User.schema.methods in either file, it shows the following:
[0] {
[0] getSnapshot: [AsyncFunction (anonymous)],
[0] getLastTweetId: [AsyncFunction (anonymous)]
[0] }
It looks like my first guess regarding what was wrong was on point, and I was just sloppy in verifying that I'm instantiating the user correctly.
const user = await User.find({twitterId})
The above line was returning an array of users.
Instead, I should have called:
const user = await User.findOne({twitterId})
I did not detect the bug at first, because logging an array that contains only one object looks nearly the same as just logging the object itself, I simply overlooked the square brackets.
Changing that single line fixed it.
I recently updated a weather dashboard project that I was working on to have a server backend, that I could just pull the API json values from said server page (in order to hide the api key I need to utilize the weather api). Since my React project requires entering a searched city value or zipcode and the API request being made requires said searched value to be submitted into the site request, I am struggling with how to get said value from the client side to the server side. Whenever I have tried to connect a callback function which can just grab the value from the React component after the user enters it, I get a React error that states
"Module not found: You attempted to import which falls outside of the project src/ directory. Relative imports outside of src/ are not supported"
My question is, how am I meant to connect this?
Below is my code for the component collecting the search value, overview.js, where the function getSearch receives the value from a child component:
import React from 'react';
import './overview.css';
import { RecentSearches } from '../Recent Searches/recentSearches';
import { Hourly } from '../Hourly/hourly';
import { Fiveday } from '../5 Day Forecast/fiveday';
export function Overview() {
// this callback function receives the searched city entered from recentSearches and applies it to fetchForecast
const getSearch = async (searchedCity) => {
console.log(searchedCity);
fetchForecast(searchedCity);
};
async function fetchForecast(city) {
var BASE_URL = `localhost:8000/forecast`;
const response = await fetch(BASE_URL);
const data = await response.json();
// collects all of the current weather info for your search
const currentTempInfo = {
city: data.location.name,
state: data.location.region,
epochDate: data.location.localtime_epoch,
message: data.current.condition.text,
wicon: data.current.condition.icon,
currentTemp: data.current.temp_f,
currentHighTemp: data.forecast.forecastday[0].day.maxtemp_f,
currentLowTemp: data.forecast.forecastday[0].day.mintemp_f,
feelsLike: data.current.feelslike_f,
humidity: data.current.humidity,
rainLevel: data.current.precip_in,
// hourlyTemps is an array, starts from midnight(index 0) and goes every hour until 11 pm(index 23)
hourlyTemps: data.forecast.forecastday[0].hour.map((entry) => {
let epochTime, temp;
[epochTime, temp] = [entry.time_epoch, entry.temp_f];
return [epochTime, temp];
})
};
// console.log(currentTempInfo);
const daycardInfo = [];
// this for loop triggers and creates an array with all necessary values for the
function daycardForLoop() {
for (let x=0; x < 3; x++) {
const fcDayDates = data.forecast.forecastday[x].date_epoch;
const dayInfo = data.forecast.forecastday[x].day;
const dayValues = {
dates: fcDayDates,
message: dayInfo.condition.text,
wicon: dayInfo.condition.icon,
maxTemp: dayInfo.maxtemp_f,
minTemp: dayInfo.mintemp_f,
avgTemp: dayInfo.avgtemp_f
};
// pushes dayValues object into daycardInfor array
daycardInfo.push(dayValues);
};
};
daycardForLoop();
// this updates the state with the forecast for the city entered
const newData = {
currentTempInfo: currentTempInfo,
daycardInfo: daycardInfo
};
// this spits out the newly created forecast object
return newData;
};
return (
<div>
<div className='jumbotron' id='heading-title'>
<h1>Welcome to <strong>Weathered</strong>!</h1>
<h3>A Simple Weather Dashboard </h3>
</div>
<div className='container-fluid' id='homepage-skeleton'>
<div className='d-flex' id='center-page'>
<RecentSearches getSearch={getSearch}/>
<Hourly />
</div>
</div>
<Fiveday />
</div>
)
};
Here is my server code, index.js, where you can see that I need to fill in the params value of "q" that you can find in the get request for the "/forecast" page:
const PORT = 8000;
const express = require('express');
const cors = require('cors');
const axios = require('axios');
require('dotenv').config();
const app = express();
// this callback function receives the searched city entered from recentSearches and applies it to fetchForecast
// update: this callback function now passes the search to the backend for the url search to parse the new data
// export const getSearch = async (searchedCity) => {
// fetchForecast(searchedCity);
// };
app.get('/', (req, res) => {
res.json('hi');
});
app.get('/forecast', (req, res) => {
const options = {
method: 'GET',
url: `http://api.weatherapi.com/v1/forecast.json?key=${process.env.REACT_APP_API_KEY}`,
params: {
q: "*** I need to get the search value here ***",
days: 3,
api: "no",
alerts: "no"
}
};
axios.request(options).then((response) => {
res.json(response.data);
}).catch((error) => {
console.log(error);
});
});
app.listen(PORT, () => console.log(`Server running on PORT ${PORT} `))
Apologies if the message itself is convoluted. Any help/tips/comments are much appreciated!
I'm trying to create a course registration system using a form to send student information to a database. I created Express routes to create new entries in a mongo collection by post request, and it works fine when I test it with Postman and hardcoding the JSON I want to submit into the route. But when I created an HTML form with javascript that uses axios to send the same JSON to the post, something goes wrong. It successfully passes the data to the route, but something happens where it ends up stopping at some point in the code and runs twice, throwing an error. When I try to get all data from the database after, the entries I posted show up, meaning it was successfully entered but the problem is still something I want to work out.
This is the route I created
const express = require("express");
const router = express.Router();
const data = require("../data");
const studentData = data.students;
const courseData = data.courses;
router.post("/", async (req, res) => {
/* new students added by post request in the JSON form
{
"userName": "testUser",
"hashedPass": "testPass",
"profile": {
"lastName": "testLast",
"firstName": "testFirst",
"studentId": 12345678,
"year": 2,
"credits": 0
}
}
*/
try {
const sData = req.body;
const newStudent = await studentData.create(sData.userName,
sData.hashedPass, sData.profile.lastName, sData.profile.firstName,
sData.profile.studentId, sData.profile.year, sData.profile.credits);
res.status(200).json(newStudent);
} catch (e) {
console.log(e);
res.status(400).json({ error: "post /students" });
}
});
And this is the function to create a new student
const mongoCollections = require("./mongoCollections");
const students = mongoCollections.students;
const uuid = require("node-uuid");
const create = async (user, pwd, last, first, studentId, year, credits) =>{
const studentCollection = await students();
const id = uuid.v4();
if (!user || typeof(user) !== "string") throw "You must provide a username";
if (!pwd || typeof(pwd) !== "string") throw "You must provide a password";
if (!last || typeof(last) !== "string") throw "You must provide a last name for your student";
if (!first || typeof(first) !== "string") throw "You must provide a first name for your student";
if (!studentId || typeof(studentId) !== "number") throw "You must provide a student ID";
if (!year || typeof(year) !== "number") throw "You must provide a year";
if(credits !== 0){
if (!credits || typeof(credits) !== "number") throw "You must provide number of completed credits";
}
const newstudent = {
_id: id,
userName: user,
hashedPass: pwd,
profile: {
_id: id,
lastName: last,
firstName: first,
studentId: studentId,
year: year,
credits: credits,
finishedCourses: [],
currentCourses: [],
holds: false
}
};
const insertInfo = await studentCollection.insertOne(newstudent);
if (insertInfo.insertedCount === 0) console.log("Could not add student");
const newId = insertInfo.insertedId;
const student = await get(newId);
return student;
};
This all works fine when I send the JSON via Postman or set sData = the object I want to pass. But I created a simple HTML page with forms to submit the data from the client, but when I submit the form something strange happens. It sends the object to the route and the route receives it properly, meaning if I console.log(sData) at that point it will print the information I gave it. But then it stops running at the line
const studentCollection = await students();
and seemingly runs the post route again, but this time with an empty object. At that point it continues and runs through the rest of the code, and finishes but still appears to add the correct data to the collection, but the browser changes to a blank page with the error "{"error":"post /students"}" from my route. This is the HTML I'm using.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script type="text/javascript">
function getForm(){
axios.get('/students');
}
function submitform(){
axios.post('/students',
{
"userName": "testUser",
"hashedPass": "tesPass",
"profile": {
"lastName": "testLast",
"firstName": "testFirst",
"studentId": 12345678,
"year": 2,
"credits": 0
}
}
);
}
</script>
</head>
<body>
<form method="post" action="/students">
<button type="submit" onclick="submitform()">submit</button>
</form>
<form method="get" action="/students">
<button type="submit" onclick="getForm()">submit</button>
</form>
</body>
</html>
If it helps, this is my index.js from /routes
const studentRoutes = require("./students");
const courseRoutes = require("./courses");
const express = require("express");
const app = express();
const constructorMethod = app => {
app.use("/students", studentRoutes);
app.use("/courses", courseRoutes);
app.use("*", (req, res) => {
res.status(404).json({ error: "Not found" });
});
};
constructorMethod(app);
module.exports = constructorMethod;
and this is my main app.js
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const configRoutes = require("./routes");
const path = require('path');
app.use(bodyParser.json());
app.get("/", (req,res) => {
res.sendFile(path.resolve(__dirname, "index.html"));
});
configRoutes(app);
app.listen(3000, () => {
console.log("We've now got a server!");
console.log("Your routes will be running on http://localhost:3000");
});
Sorry if any of this is ignorant, I'm new to requests.
Any help would be great.
I'm running this all on localhost
Update:
I figured out that trying to post several times through the HTML form changes the issue slightly. It passes all of the data to the route, which creates a new student and runs all the code without issue. Then after completion, it seemingly runs again with an empty object, stopping when it reaches
const studentCollection = await students();
again. It still throws the same "{"error":"post /students"}" error
<div>
<button onclick="submitform()">submit</button>
</div>
<div>
<button onclick="getForm()">submit</button>
</div>
You don't need form tag, All operation is performed onclick,
Why? Runs twice
The javascript submitform function is calling POST api.
Also <form action="POST" ... called POST api
I am trying to implement a logger in node js which will create a new log file every day on a custom format for logs
for this i have used three packages
winston
morgan
winston-daily-rotate-file
so the final output should every day a new log file should create in logs folder and it should log all the http(morgan logs) and typed logs (winston logs) into a single file in the below format
Date || Filename || statusCode || logMessage || uuid (for tracing)
eg: Fri Jan 18 2019 13:48:18 GMT+0530 (IST) || [index.js] || 200 || calling the new route || 287dccb0-1afa-11e9-88a0-dfb1c665be9d
so for this i have written three files index.js(root file of nodejs) logger.js(logger implementation and configuration) and logger.test.js(test cases for logger using jest)
additional packages
cors
uuid
http-context
app-root-path
express-http-context
jest
the problems that i have
if i put a logger.error({message: {statusCode:200, logMsg: "the server will be starting in port 3000"}}) in the index.js on app.listen before to console.log() the uuid is null
the test cases that i have written is wrong, i am new to jest i just want to know how can i check all that cases.
why when i test the suits uuid is null, how can i pass the uuid for test cases also
how can i check whether new folder will be created and if already logs folder are there new file is created kind of test cases.
How can i add other levels , info, debuge , warn based on the env. How can i improve this code to implement the logger functionality
// index.js
const app = require('express')();
const cors = require('cors')
const morgan = require('morgan') // HTTP request logger middleware
const logger = require('./config/logger')(module) //Logger
const uuid = require('uuid')
const httpContext = require('express-http-context')
// Use any third party middleware that does not need access to the context here
// app.use(some3rdParty.middleware);
app.use(httpContext.middleware);
// all code from here on has access to the same context for each request
// Run the context for each request.
// Assigning a unique identifier to each request
app.use((req, res, next) => {
httpContext.set('reqId', uuid.v1());
next()
})
// using morgan with winston(logger)
app.use(morgan('combined', {
stream: {
write: (message) => logger.error(message)
}
}))
app.use(cors());
app.use("/new", (req, res) => {
logger.error({
message: {
statusCode: 400,
logMsg: "hitting new route"
}
})
nextLayer(res)
})
const nextLayer = (res) => {
logger.error({
message: {
statusCode: 400,
logMsg: "hitting in nextLayer function"
}
})
res.send("OK")
}
app.listen(4000, () => {
console.log('Server running on port 4000');
})
// Logger.js
const appRoot = require('app-root-path')
const {
createLogger,
format,
transports
} = require('winston')
const {
combine,
timestamp,
label,
printf
} = format
const path = require('path')
require('winston-daily-rotate-file');
const httpContext = require('express-http-context')
/**
* #method checkMessageProp
* #param {message} can be object if developer defined, else it will be string
* if its a network request
* #returns a fixed format how the status code and message should show
*/
const checkMessageProp = (message) => {
switch (typeof message) {
case "object":
const {
statusCode,
logMsg
} = message
return `${statusCode ? statusCode : "Not Defined"} || ${logMsg ? logMsg : "Not Defined"}`;
case "string":
let messageSplit = message.split(`"`)
var message = messageSplit ? `${messageSplit[2].trim().split(" ")[0]} || ${messageSplit[1]}` : null
return message
default:
return message
}
}
/**
* #method customFormat
* #param {log} the log passed by the developer or based on network requests
* #returns a customFormat how it should be logged to the log files
*/
const customFormat = printf(log => {
const now = new Date();
const reqId = httpContext.get('reqId');
return `${log.timestamp ? new Date(log.timestamp) : now} || [${log.label}] || ${checkMessageProp(log.message)} || ${reqId ? reqId : null}`
});
/**
* #method getFileName
* #param {moduleObj} the module realted object passed from the require of logger file
* #returns the file name where the logger was invoked
*/
const getFileName = moduleObj => {
if (Object.keys(moduleObj).length > 0) {
let parts = moduleObj.filename.split(path.sep)
return parts.pop()
} else {
return "Module not passed while requiring the logger"
}
}
// Custom settings for each transport
const options = moduleObj => {
return {
dailyRotateFile: {
filename: `${appRoot}/logs/TPS-UI-%DATE%.log`,
datePattern: 'YYYY-MM-DD',
prepend: true,
level: "error",
timestamp: new Date(),
localTime: true
}
}
}
// Instantiate a Winston Logger with the settings
let logger = moduleObj => {
return createLogger({
format: combine(
label({
label: getFileName(moduleObj)
}),
customFormat
),
transports: [
new transports.DailyRotateFile(options(moduleObj).dailyRotateFile)
],
exitOnError: false // do not exit on handled exceptions
})
}
module.exports = logger
// logger.test.js
const logger = require('./logger')
beforeEach(() => {
mockLoggerMessageObject = {
message: {
statusCode: 400,
logMsg: "Calling in test suite"
}
}
mockLoggerMessageString = `::ffff:127.0.0.1 - - [18/Jan/2019:04:50:57 +0000]
"GET /new HTTP/1.1" 200 2 "http://localhost/" "Mozilla/5.0
(linux) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/11.12.0"`
mockLoggerMessageNumberFormat = 123
mockLoggerMessageArrayFormat = ["data", "test", 123]
})
describe(`Logger test cases`, () => {
test('should invoke the logger function with the mock Logger message object', () => {
expect(logger(module).error(mockLoggerMessageObject)).toBeDefined()
})
test(`should invoke the logger function with empty object`, () => {
expect(logger(module).error({})).toBeDefined()
})
test(`should invoke the logger function without any module object`, () => {
expect(logger({}).error(mockLoggerMessageObject)).toBeDefined()
})
test(`should invoke the logger function without any module and message object`, () => {
expect(logger({}).error({})).toBeDefined()
})
test(`should invoke the logger function with the http request`, () => {
expect(logger(module).error(mockLoggerMessageString)).toBeDefined()
})
test(`should invoke the logger function with the number format`, () => {
expect(logger(module).error(mockLoggerMessageNumberFormat)).toBeDefined()
})
test(`should invoke the logger function with the array format`, () => {
expect(logger(module).error(mockLoggerMessageArrayFormat)).toBeDefined()
})
})
for winston i'm using timestamp(), like this it will automatically add timestamp() property to the object
const {transports, createLogger, format} = require('winston');
const logger = createLogger({
format: format.combine(
format.timestamp(),
format.json()
),
Also to check if it creates file you can mock date, to say 2019-01-01 and check is it create file 2019-01-01.log
than move date to 2019-01-02 and log something else.
Winston will create new folder and gzip archive and you can check is file exists and can be unzipped and contains information
Try to read winston's documentation.
Basically i would say that you may need to use
format.timestamp()
format.json()
colorize()
dailyRotate with zippedArchive:true
If morgan doesn't suits your needs you can try to log directly in
app.use((req, res, next) => {
logger.silly({ message:'start', req,res});
return next().then(r=>logger.silly({ message:'end', req,res}; return r;);
}
Possible duplicate. Not sure.
connections: {
connectionID : {
userID: true,
anotherUserID: true
},
users: {
userID : {
deviceToken : "tokenID",
name : "Display Name"
},
anotherUserID : {
deviceToken : "tokenID",
name : "Display Name"
}
}
and so on and so forth.
This is my index.js:
exports.sendConnectionNotification = functions.database.ref('/connections/{connectionID}/{userID}').onWrite(event => {
const parentRef = event.data.ref.parent;
const userID = event.params.userID;
const connectionID = event.params.connectionID;
// If un-follow we exit the function.
if (!event.data.val()) {
return console.log('Connection', connectionID, 'was removed.');
}
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database().ref('/users/${userID}/deviceToken').once('value');
// Get the user profile.
const getUserProfilePromise = admin.auth().getUser(userID);
and it continues. I am getting this error in my logcat:
Error: Firebase.child failed: First argument was an invalid path: "/users/${userID}/deviceToken". Paths must be non-empty strings and can't contain ".", "#", "$", "[", or "]"
at Error (native)
at Ge (/user_code/node_modules/firebase-admin/lib/database/database.js:111:59)
at R.h.n (/user_code/node_modules/firebase-admin/lib/database/database.js:243:178)
at Fd.h.gf (/user_code/node_modules/firebase-admin/lib/database/database.js:91:631)
at exports.sendConnectionNotification.functions.database.ref.onWrite.event (/user_code/index.js:31:51)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:35:20
at process._tickDomainCallback (internal/process/next_tick.js:129:7)
I do not understand why Firebase is not able to reach the node. Clearly, my path is valid. Where am I going wrong? Sorry, I happen to start learning Firebase Functions just today.
**EDIT 1: **
After replacing:
const getDeviceTokensPromise = admin.database().ref('/users/${userID}/deviceToken').once('value');
with
const getDeviceTokensPromise = admin.database().ref(`/users/${userID}/deviceToken`).once('value');
I have gotten a new error. My console log displays:
There are no notification tokens to send to.
Here is my full index.js:
// // Create and Deploy Your First Cloud Functions
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
/**
* Triggers when a user gets a new follower and sends a notification.
*
* Followers add a flag to `/followers/{followedUid}/{followerUid}`.
* Users save their device notification tokens to `/users/{followedUid}/notificationTokens/{notificationToken}`.
*/
exports.sendConnectionNotification = functions.database.ref('/connections/{connectionID}/{userID}').onWrite(event => {
const parentRef = event.data.ref.parent;
const userID = event.params.userID;
const connectionID = event.params.connectionID;
// If un-follow we exit the function.
if (!event.data.val()) {
return console.log('Connection', connectionID, 'was removed.');
}
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database().ref(`/users/${userID}/deviceToken`).once('value');
// Get the user profile.
const getUserProfilePromise = admin.auth().getUser(userID);
return Promise.all([getDeviceTokensPromise, getUserProfilePromise]).then(results => {
const tokensSnapshot = results[0];
const user = results[1];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
console.log('There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.');
console.log('Fetched user profile', user);
// Notification details.
const payload = {
notification: {
title: `${user.userNickName} is here!`,
body: 'You can now talk to each other.'
}
};
// Listing all tokens.
const tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload).then(response => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
});
});
You can do use (`) instead of (') as i was also having same problem and solved by using this.
thanks
Change
const getDeviceTokensPromise = admin.database().ref('/users/${userID}/deviceToken').once('value');
to
const getDeviceTokensPromise = admin.database().ref('/users/' + userID + '${userID}/deviceToken').once('value');
'/users/${userID}/deviceToken' is not a valid path.
but '/users/123456/deviceToken' where 123456 represents the user ID, is.
maybe you are using single quote instead of back-ticks.
https://developers.google.com/web/updates/2015/01/ES6-Template-Strings
so the path is not concatenated in a right way.