Not sending push notifications - javascript

I have a problem regarding push notifications. I have this error in firebase functions logs:
Error: To send a message with a payload, the subscription must have
'auth' and 'p256dh' keys.
exports.storePostData = functions.https.onRequest(
(request, response) => {
cors(request, response, () => {
admin.database().ref('posts').push({
id: request.body.id,
title: request.body.title,
location: request.body.location,
image: request.body.image
}).then(() => {
webpush.setVapidDetails('mailto: xxxx#gmail.com', 'BLl7xIPAyJNzsMi5vo_aG-4RdXdyZ4Q4ZFpTgnm902qN79MIiSORBk9N-rfFEGiKNPuJu5SJmUX35Wwce9nuH94', 'M8E6hw7jCmu7qNQJ88FV5o02OAiLefEFJK8jyJimk7g')
return admin.database().ref('subscriptions').once('value');
}).then(subscriptions => {
subscriptions.forEach(sub => {
var pushConfig = {
endpoint: sub.val().endpoint,
keys: {
auth: sub.val().keys,
p256dh: sub.val().p256dh
}
}
webpush.sendNotification(pushConfig, JSON.stringify({
title: 'New Post',
content: 'New post added',
openUrl: '/help'
})).catch(err => {
console.log(err)
})
})
response.status(201).json({
message: 'Data stored',
id: request.body.id
})
}).catch(err => {
response.status(500).json({
error: err
})
})
})
});
This is my function for storing post data, and i think the problem is here because it can't even get to push event in serviceWorker (i don't get any logs there).

On the client side, you should be able to retrieve subscription
On Angular it would be like
const sw = await navigator.serviceWorker.register('/assets/js/service-worker.js', { scope: '/assets/js/' });
let subscription = await sw.pushManager.getSubscription();
Through this way, you should be able to get subscription and subscription object contains p256dh and auth keys.
You should be able to see the values by
console.log("subscription: ", subscription?.toJSON());

Ye i figured it need to go as:
keys: {
auth: sub.val().keys.auth,
p256dh: sub.val().keys.p256dh
}

Related

How to check if there are no more documents to update using findOneAndUpdate

So I am learning CRUD for a school project and I followed a tutorial that was really useful. However, when I completed it I noticed that when there are no more quotes to update, it still updates quotes. How can I change this so that it will stop updating quotes that arent even there?
app.put('/quotes', (req, res) => {
quoteCollection.findOneAndUpdate(
{ name: 'Yoda' },
{
$set: {
name: req.body.name,
quote: req.body.quote
}
},
{upsert: true}
)
.then(result => {
//The if block that i am trying
if (result.deletedCount === 0) {
return res.json('No quote to delete')
}
})
.catch(error => console.error(error))
})
Why are you passing {name: "Yoda}? This route is supposed to only update the quote with "Yoda" as its name? If not, then you need to grab from the request object the quote that should be updated.
I tried to create a different version, based on the assumption that the quote that should be updated will come from the req.body:
app.put("/quotes", async (req, res) => {
//Grab the name/id/identifier for the quote you want to update from the body
const query = req.body.name;
// Try to update the document on the database
try {
const result = await quoteCollection.findOneAndUpdate(
query,
{
name: req.body.name,
quote: req.body.quote,
},
{
upsert: true,
new: true,
}
);
// If it worked, it will return the updated quote
res.status(200).json({
status: 200,
data: {
result,
},
});
} catch (err) {
res.status(400).json({
status: 400,
message: "Something went wrong",
});
}
});

How to get message property of an error returned from an express server

I'm returning an error response from express like below.
router.post("/", authCheck, authCheckAdmin, (req, res, next) => {
const burger = new Burger({
_id: mongoose.Types.ObjectId(),
...req.body
});
console.log(burger);
Burger.find({ name: req.body.name })
.exec()
.then(result => {
if (!result) {
burger
.save()
.then(result => {
res.status(201).json({
message: "Burger Successully Created",
burger: result
});
})
.catch(error => {
res.status(500).json(new Error('Something went wrong when saving the burger')); // <------
});
} else {
res.status(500).json(new Error('Burger already exist'));
}
});
});
This is the React code
export const addBurger = burgerData => (dispatch, getState) => {
const currentState = getState();
axios
.post("/burgers", burgerData, {
headers: {
Authorization: `Bearer ${currentState.auth.token}`
}
})
.then(response => {
console.log(response);
})
.catch(error => {
console.error(error.message); <-------- Here
});
};
Below is the console preview
How can I get the message that I have passed inside express? I want to get "Burger already exist" as the error message. Is there another way to do this? Thanks
Server side fix
res.status(500).send({ error: 'Something failed!' })
According to me, error is not defined in the last else of your Burger.find method which is causing this error. This type of error's are mostly server side errors. Hope this work for you.
res.status(500).json(new Error('Burger already exiist')); // <------
if you would look at response in dev tools, you see that in this case payload is empty object. So you need directly tell the server what you need to send back. Try this if you already waiting for message key in react part:
res.status(500).json({message: 'Burger already exist'});

Error: Received unknown parameter: source

I'm making a react-redux app with firetore as database. Now, I wanted to use firebase cloud functions for handling stripe payments.
Here is the cloud function "createSubscription":
exports.createSubscription = functions.database
.ref("/teachers/{userId}/pro-membership/token")
.onWrite((event, context) => {
const tokenId = event.after.val();
const userId = context.params.userId;
if (!tokenId) throw new Error("Token Missing");
return admin
.database()
.ref(`teachers/${userId}`)
.once("value")
.then(snapshot => snapshot.val())
.then(user => {
console.log(user);
return stripe.subscriptions.create({
customer: user.customerId,
source: tokenId, **// Here is the error occuring**
items: [
{
plan: "pro-membership"
}
]
});
})
.then(sub => {
admin
.database()
.ref(`teachers/${userId}/pro-membership`)
.update({
status: "active"
});
})
.catch(err => {
console.log("ERRor", err);
});
});
Below is the error information from cloud function's logs:
source is not a valid parameter on a stripe.subscriptions.create request, see https://stripe.com/docs/api/subscriptions/create
Try updating the customer first, adding the token, https://stripe.com/docs/api/customers/update, then create a subscription!

Node.js RESTful API validation error on array schema

I am creating a RESTful API using Node.js and mongoose by following the tutorial by Acedemind. I have got it working just fine and am now expanding it to allow the client to post several products in the same order. Basically I am modifying a simple "POST" request to be an array instead of some variables. My problem is that I run into a long validation error that hinders the array from being created. Here is the code for the post request:
router.post("/", async (req, res, next) => {
const productsMaybeFalses = await Promise.all(req.body.products.map(async ({ productId })=> {
const product = await Product.findById(productId);
if (!product) {
return false;
}
return {
...product,
productId,
}
}));
const errors = productsMaybeFalses
.map((productMaybeFalse, index) => {
return {
productMaybeFalse, index
}
})
.filter(({ productMaybeFalse }) => !productMaybeFalse)
if (errors.length) {
console.log(error);
return;
}
console.log(productsMaybeFalses);
const products = productsMaybeFalses
.filter((productMaybeFalse) => productMaybeFalse);
const order = new Order({
_id: mongoose.Types.ObjectId(),
products: products
});
return order.save().then(results => {
console.log(results);
res.status(201).json(results.map((result) => ({
message: "order stored",
createdOrder: {
_id: result._id
},
request: {
type: "GET",
url: "http://localhost:3000/orders/" + result._id
}
})));
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
})
})
})
And here is the Schema for the Order:
const mongoose = require("mongoose");
const pSchema = mongoose.Schema({
productId: { type: mongoose.Schema.Types.ObjectId, ref: "Product", required: true},
quantity: { type: Number, default: 1}
});
const orderSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
products: [pSchema]
});
module.exports = mongoose.model("Order", orderSchema)
To keep this question from being too long I will only post the end part of the error. The part that I feel tells the most information abut the problem. If anyone wants the whole error message to better understand the problem and maybe come up with a solution for me I will be very willing to post it as well. Here it is:
kind: 'Array',
value: [Array],
path: 'products',
reason: TypeError: value[i].toObject is not a function
at DocumentArray.cast (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/schema/documentarray.js:309:27)
at DocumentArray.SchemaType.applySetters (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/schematype.js:755:12)
at model.$set (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:922:18)
at model._handleIndex (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:740:14)
at model.$set (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:697:22)
at model.Document (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/document.js:114:12)
at model.Model (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/model.js:73:12)
at new model (/Users/axelhagman/Documents/Jacobs/node_modules/mongoose/lib/model.js:4324:13)
at router.post (/Users/axelhagman/Documents/Jacobs/api/routes/orders.js:70:17)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:189:7) } },
_message: 'Order validation failed',
name: 'ValidationError' }
POST /orders/ 500 440.085 ms - 7622
I am very new to using node.js and creating API overall so any help would be very much appreciated. Thanks!

Function not getting called in Mocha test

I am working on an Express.js app. The current feature is creating an appointment with a post request and getting and saving data from third party API, then sending updated API data in the subsequent request. The feature is fully working but in the test, the function to get API data is not getting called.
Route to create appointment:
app.post('/schedule', requestHandler.postSchedule);
The request handler for creating appointment:
requestHandler.postSchedule = function (req, res) {
let appointment = {
// posted data
};
new Appointment(appointment)
.save()
.then(newAppointment => {
if(req.body.cityName && req.body.cityName !== '') {
console.log('req.body.cityName', req.body.cityName);
weatherHelper.addNewCityWeatherData(req.body.cityName);
}
return newAppointment;
})
.then(newAppointment => {
// do some other stuff
res.send(newAppointment);
})
.catch(err => {
error(err);
});
};
Function to add weather data:
exports.addNewCityWeatherData = (city) => {
console.log('City in addNewCityWeatherData', city);
getCurrentTrackingCities(cities => {
if(cities.indexOf(city) < 0) {
console.log(city + ' data not in weather');
getWeatherData(city, data => {
console.log('Got weather data');
addWeatherDataToDB(city, data);
});
} else {
console.log('City exists');
}
});
};
Function to get weather data from API:
getWeatherData = (city, callback) => {
console.log('getWeatherData called', city);
let url = `http://api.apixu.com/v1/forecast.json?key=${weatherApiKey}&q=${city}&days=${10}`
request(url, (err, res, body) => {
console.log('Weather data received body');
callback(body);
});
};
When testing, this feature fails and all console logs are printed except the 'Weather data received body' and the logs in consequent functions.
Here is my test:
describe.only('Weather data', function() {
let requestWithSession = request.defaults({jar: true});
let hashedPass = bcrypt.hashSync('testpass', null);
beforeEach((done) => {
new User({
'name': 'Test User',
'email': 'testuser#test.com',
'password': hashedPass
})
.save()
.then(() => {
let options = {
'method': 'POST',
'uri': testHost + '/login',
'form': {
'email': 'testuser#test.com',
'password': 'testpass'
}
};
requestWithSession(options, (err, res, body) => {
done();
});
});
}); // beforeEach
afterEach((done) => {
// remove test stuff from db
}); // afterEach
it('Adds weather data when an appointment with new city is posted', (done) => {
let options = {
'method': 'POST',
'uri': testHost + '/schedule',
'form': {
'title': 'Test title',
'description': 'Test description',
'start_date_time': '2017-07-19 01:00',
'end_date_time': '2017-07-19 02:00',
'cityName': 'New York',
'isTrackingWeather': 'true'
}
};
// post request to add appointment data
requestWithSession(options, (err, res, body) => {
if(err) {
console.log('DatabaseError in Weather Data');
throw {
type: 'DatabaseError',
message: 'Failed to create test setup data'
};
}
let options = {
'method': 'GET',
'uri': testHost + '/allweather'
};
// subsequesnt request to get updated weather data
requestWithSession(options, (err, res, body) => {
let found = false;
weatherData = JSON.parse(body);
// console.log('weatherData in test', weatherData);
weatherData.forEach(weather => {
if(weather.location && weather.location.name === 'New York') {
found = true;
}
});
expect(found).to.be.true;
done();
});
});
});
}); // Weather Data
Here is the terminal output:
Can anyone please tell me what am I doing wrong?
When you run your test is that the test suite make a request to your test server, and the code that handles the request in your test server makes another request to another host.
You do not get to see 'Weather data received body' because the request handled by your test server is not waiting for the request that the test server itself makes. addNewCityWeatherData has no callback and does not return a promise, so the code that calls it goes on its merry way without waiting for it to complete. You should modify it to allow for the calling code to wait for a result.
Also, I'm not seeing how the data from the request initiated by your test server is folded back into the request that comes from your test suite. You may have to add some code for that too, unless addWeatherDataToDB(city, data); is taking care of it automatically somehow.

Categories