I'm having a strange problem with Parse-Server.
I get sometimes the following error:
ParseError { code: 101, message: 'Object not found.' }
It usually happen after an error in cloud code, in whatever function that use the object even if there is no update in it (like the following function that just doing a find but still create the bug if it fail for any reason like a bad code line). However I'm not sure this is the cause.
I have that simple function in Cloud Code:
const user = req.user
const card_id = cid(req, user)
const base_error = global.i18n.__('errors.default')
if(!card_id) return res.error(base_error)
console.log('CID: ' + card_id + ' | UID: ' + user.id)
new Parse.Query(global.m.Card).get(card_id)
.then((card) => {
if((new Date) - card.updatedAt > OSC.syncDelay)
return _CARD.sync(card.get('onum'))
return card
})
.then((card) => {
res.success(card)
})
.then(null, (error) => {
if(Array.isArray(error)) error = error[0]
console.log(error) // HERE is where the error is logged
res.error(base_error)
})
Here is what the console.log print:
CID: PqOMwi5y60 | UID: QldBflokJV
Here is how the Card ACLs are defined (with an update, not at creation):
const cardACL = card.getACL()
cardACL.setReadAccess(user, true)
cardACL.setWriteAccess(user, true)
if(memory.admin) {
cardACL.setReadAccess(memory.admin, true)
cardACL.setWriteAccess(memory.admin, true)
}
card.setACL(cardACL)
Finally, this is what I have in my Database for the card object Permissions/ACLs:
"_id" : "PqOMwi5y60",
"_wperm" : [
"role:Administrator",
"QldBflokJV"
],
"_rperm" : [
"role:Administrator",
"QldBflokJV"
],
"_acl" : {
"role:Administrator" : {
"w" : true,
"r" : true
}
}
Am I doing something wrong ?
Because your ACL contains the user (since only the user or admin can read and write from/to the object) in your query you need also to send the session token of the logged in user. So at the end your code should look like the following:
const user = req.user
const card_id = cid(req, user)
const base_error = global.i18n.__('errors.default')
if(!card_id) return res.error(base_error)
console.log('CID: ' + card_id + ' | UID: ' + user.id)
new Parse.Query(global.m.Card).get(card_id,{
sessionToken: request.user.get("sessionToken")
})
.then((card) => {
if((new Date) - card.updatedAt > OSC.syncDelay)
return _CARD.sync(card.get('onum'))
return card
})
.then((card) => {
res.success(card)
})
.then(null, (error) => {
if(Array.isArray(error)) error = error[0]
console.log(error) // HERE is where the error is logged
res.error(base_error)
})
Please notice that i sent also the session token in the query.
Related
I have an AWS Cognito user pool that is a replacement for an old one that I had to delete because of a custom attribute issue. I am using the same exact code as before though the keys have changed. I have them in a JSON file that I pulled from the AWS URL for getting the keys. I am getting an error now about invalid signature when trying to validate a JWT. I know my code is solid since it hasn't changed but was looking to see from others if there is something else I am missing or should do other than update my pool id, client id, and keys.json file.
Edit 2
I have resolved the issue by deleting and recreating the user pool - I use Terraform so it is quick and easy but also repeatable so this is the exact same config as before but now working
Edit adding my code just incase there is an issue with it though I can't see why if nothing changed
exports.isJWTValid = () => (req, res, next) => {
let idToken = req.headers.authorization
let token = idToken.split(' ')[1]
let header = jwt_decode(token, { header: true });
let keys = keysJSON.keys
let kid = header.kid
let jwk = keys.find(r => r.kid === kid)
let pem = jwkToPem(jwk);
jwt.verify(token, pem, { algorithms: ['RS256'] }, function(err, decodedToken) {
if(err) { // error is showing up in this if(err) and returning to postman
logger.debug(err)
return res.status(401).json({success: false, err})
}
const currentSeconds = Math.floor((new Date()).valueOf() / 1000)
if (currentSeconds >= decodedToken.exp || currentSeconds < decodedToken.auth_time ) {
let message = 'Session has expired, please login again.'
return res.status(401).json({success: false, message});
}
if(decodedToken.aud !== config.ClientId) {
let message = 'Token doen\'t match app client'
return res.status(401).json({success: false, message});
}
if(decodedToken.iss !== `https://cognito-idp.us-east-1.amazonaws.com/${config.UserPoolId}`) {
let message = 'Token doen\'t match user pool'
return res.status(401).json({success: false, message});
}
if(decodedToken.token_use !== 'id' && decodedToken.token_use !== 'access') {
let message = 'Token use case doen\'t match'
return res.status(401).json({success: false, message});
}
logger.debug('decodedToken: ', decodedToken)
next()
});
};
Trying to measure performance and output performance measures by using performance.getEntriesByType() however getting back an error that performance.getEntriesByType() is not a function. Tried different troubleshooting however anything I tried results in the same error, don't really understand how getEntriesByType() is not a function as it comes from a node dependency. Node version I am using is : 10.22.1 not using any npm packages pure NodeJs code.
var {performance} = require('perf_hooks');
var util = require('util');
var debug = util.debuglog('performance');
handlers._tokens.post = function (data, callback){
performance.mark('entered function')
var phone = typeof(data.payload.phone) == 'string' && data.payload.phone.trim().length > 10 ? data.payload.phone.trim() : false
var password = typeof(data.payload.password) == 'string' && data.payload.password.trim().length > 0 ? data.payload.password.trim() : false
performance.mark('inputs validated')
if (phone && password){
performance.mark('beginning user lookup')
_data.read('users', phone, function(err, userData){
performance.mark('user lookup complete')
if(!err && userData){
// hash the password and validate
performance.mark('beginning password hashing')
hashedPassword = helpers.hash(password)
performance.mark('password hashing complete')
if (hashedPassword == userData.hashedPassword) {
// if valid create token
performance.mark('creating data for token')
var tokenId = helpers.createRandomString(20)
// set expiry date
var expires = Date.now() + 1000 * 60 * 60
var tokenObject = {
'phone': phone,
'id': tokenId,
'expires': expires
}
performance.mark('token data creation complete')
performance.mark('beginning storing token data')
// store the token
_data.create('tokens', tokenId, tokenObject, function(err){
performance.mark('token data storing is complete')
// gather all the measurements
performance.measure('Beginning to end', 'entered function', 'token data storing is complete')
performance.measure('Password hasing', 'beginning password hashing', 'password hashing complete')
performance.measure('Finding user', 'beginning user lookup', 'user lookup complete')
performance.measure('Input validation', 'entered function', 'inputs validated')
performance.measure('Token creation', 'creating data for token', 'token data creation complete')
performance.measure('Token storing', 'beginning storing token data', 'token data storing is complete')
// log out all the measurements
var measurements = []
measuerments = performance.getEntriesByType('measure')
measurements.forEach((measurement)=>{
debug('\x1b(33m%s\x1b(0m', measurement.name + ' ' + measurement.duration)
})
if(!err){
callback(200, tokenObject)
} else {
callback(500, {'error': 'couldnt create new token'})
}
})
} else {
callback(400, {'error': 'passwords did not match'})
}
} else {
callback(400, {'error': 'couldnt find the user'})
}
})
} else {
callback(400, {'error': 'missing required fields'})
}
}
Have a look at the Documentation:
You need to create a PerformanceObserver and in its call back, you get a PerformanceObserverEntryList on which you can call getEntriesByType()
const {
performance,
PerformanceObserver
} = require('perf_hooks');
const obs = new PerformanceObserver((list, observer) => {
console.log(list.getEntriesByType('measure'));
observer.disconnect();
});
obs.observe({ entryTypes: ['measure'], buffered: true });
performance.measure('test');
(the code example is also taken from the documentation and slightly adapted to OP's question)
I'm building an iOS messenger app using Swift, Firebase and Nodejs.
My Goal:
Whenever a user sends a message and writes message data (such as senderId, receiverId, messageText) into a Firebase database inside node (/messages/{pushId}/), I want to make a message count increment by 1 using a transaction method that Firebase provides and display a notification to a receiver user.
Progress I've made so far and Problem I'm facing:
I've successfully increment message count (totalCount) using transaction method but I can't get value inside transaction result (Here's image of functions log )
I want to get "value_: 1"( this is the incremented message count) inside snapshot and put it to a badge parameter.
exports.observeMessages = functions.database.ref('/messages/{pushId}/')
.onCreate((snapshot, context) => {
const fromId = snapshot.val().fromId;
const toId = snapshot.val().toId;
const messageText = snapshot.val().messageText;
console.log('User: ', fromId, 'is sending to', toId);
return admin.database().ref('/users/' + toId).once('value').then((snap) => {
return snap.val();
}).then((recipientId) => {
return admin.database().ref('/users/' + fromId).once('value').then((snaps) => {
return snaps.val();
}).then((senderId) => {
return admin.database().ref('/user-messages/' + toId + '/totalCount').transaction((current) => {
return (current || 0) + 1
}).then((readCount) => {
console.log('check readCount:', readCount);
var message = {
data: {
fromId: fromId,
badge: //I want to display the message count here
},
apns: {
payload: {
aps: {
alert: {
title: 'You got a message from ' + senderId.username,
body: messageText
},
"content-available": 1
}
}
},
token: recipientId.fcmToken
};
return admin.messaging().send(message)
}).then((response) => {
console.log('Successfully sent message:', response);
return response;
})
.catch((error) => {
console.log('Error sending message:', error);
//throw new error('Error sending message:', error);
})
})
})
})
Does anyone know how to do this?
Thanks in advance.
The API documentation for transaction() suggests that the promise from the transaction will receive an object with a property snapshot with the snapshot of the data that was written at the location of the transaction. So:
admin.database.ref("path/to/count")
.transaction(current => {
// do what you want with the value
})
.then(result => {
const count = result.snapshot.val(); // the value of the count written
})
I have deployed the following JavaScript to Firebase Cloud Function but I am constantly getting the following error even though I know that there is a token saved under the path specified.
The idea is to trigger a notification to device when a new message is written to the database, it then fetches the registration token which is then used to try and send a notification to the user.
Error: Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array.
JS Function:
exports.notifyNewMessage = functions.database.ref('/messages/{pushId}/{message}').onCreate((snap, context) => {
const message = snap.val();
const fromId = message['fromId'];
const toId = message['toId'];
const messageTxt = message ['message'];
const imageUrl = message ['imageUrl'];
return admin.database().ref('/fcmtokens/' + toId + '/registration-tokens').once('value').then((userTok) => {
const registrationTokens = userTok.val()
console.log(registrationTokens);
return admin.database().ref('/users' + fromId).once('value').then((userDoc) => {
const senderName = userDoc.firstName //get('firstName')
const notificationBody = (imageUrl === "") ? "You received a new image message." : messageTxt
//build media messages notification
const payload = {
notification: {
title: senderName + " sent you a message",
body: messageTxt
},
data: {
SENDER_NAME: senderName,
SENDER_ID: fromId
}//end data
}//end payload
//send message
return admin.messaging().sendToDevice(registrationTokens, payload).then( response => {
const stillRegisteredTokens = registrationTokens
response.results.forEach((result, index) => {
const error = result.error
if (error) {
const failedRegistrationToken = registrationTokens[index]
console.error('blah', failedRegistrationToken, error)
if (error.code === 'messaging/invalid-registration-token'
|| error.code === 'messaging/registration-token-not-registered') {
const failedIndex = stillRegisteredTokens.indexOf(failedRegistrationToken)
if (failedIndex > -1) {
stillRegisteredTokens.splice(failedIndex, 1)
}
}
}
})//end forEach
return admin.database().ref("fcmtokens/" + recipientId).update({
registrationTokens: stillRegisteredTokens
})//end update
})//end sendToDevice
})//end return-then
})//end return-then
});
This is my fcmtokens database structure node:
"fcmtokens" : {
"dBQdpR7l1WT2utKVxdX2" : {
"registration-tokens" : {
"c4PSCAUAg5s:Yw95DyVxwElE88LwX7" : true
}
}
}
Question update:
The update database part of my function above is creating a separate branch called registrationTokens of all active tokens. I want to overwrite and update the current token under registration-tokens instead?
return admin.database().ref("fcmtokens/" + recipientId).update({
registrationTokens: stillRegisteredTokens
})//end update
Image showing the new registrationTokens branch being created.
Given your data structure, this code:
admin.database().ref('/fcmtokens/' + toId + '/registration-tokens').once('value').then((userTok) => {
const registrationTokens = userTok.val()
Leads to registrationTokens being:
{
"c4PSCAUAg5s:Yw95DyVxwElE88LwX7" : true
}
You then call sendToDevice with:
return admin.messaging().sendToDevice(registrationTokens, payload).then( response => {
But if you look at the reference documentation for `sendToDevice, it says:
registrationTokens
Array of string
An array of registration tokens for the devices to which to send the message.
And clearly your registrationTokens is not an array, but just an object.
To convert it to an array use Object.keys():
const registrationTokens = Object.keys(userTok.val())
This is a real niche question regarding Twitter OAuth with passport.js ()
I have a controller which updates the user's avatar using their Twitter "avatar":
const signInViaTwitter = (twitterProfile) => {
return new Promise((resolve, reject) => {
console.log(twitterProfile);
// find if user exist on in
User.findOne({ username: twitterProfile.username }, (error, user) => {
if (error) { console.log(error); reject(error); }
else {
// user existed on db
if (user) {
// update the user with latest git profile info
user.name = twitterProfile.displayName;
user.username = twitterProfile.username;
user.avatarUrl = twitterProfile.photos.value;
user.email = '';
// save the info and resolve the user doc
user.save((error) => {
if (error) { console.log(error); reject(error); }
else { resolve(user); }
});
}
// user doesn't exists on db
else {
// check if it is the first user (Adam/Eve) :-p
// assign him/her as the admin
User.count({}, (err, count) => {
console.log('usercount: ' + count);
let assignAdmin = false;
if (count === 0) assignAdmin = true;
// create a new user
const newUser = new User({
name: twitterProfile.displayName,
username: twitterProfile.username,
avatarUrl: twitterProfile.photos.value,
email: '',
role: assignAdmin ? 'admin' : 'user',
});
// save the user and resolve the user doc
newUser.save((error) => {
if (error) { console.log(error); reject(error); }
else { resolve(newUser); }
});
});
}
}
});
});
};
The authentication of the user works - but for some reason, the avatar won't show...here is the following console output:
Refused to load the image 'https://api.twitter.com/favicon.ico'
because it violates the following Content Security Policy directive:
"img-src https://abs.twimg.com https://*.twimg.com
https://pbs.twimg.com data:".
Does anyone know what this means? I'm thinking it's probably due to being in development mode - that is, http://localhost:8080/ ... and it won't accept https?? Or won't pass it back?
UPDATE: ^I think the above error is unrelated to the image not being display...
A little look at the html source gives:
<img class="styles__userAvatar___2x2U9" src="{unknown}" alt="Wind Up Lord Vexxos Avatar">
So it's obviously passing in an unknown variable for the src - rather than the user's display avatar...
So, for me it looks like the offending line is:
user.avatarUrl = twitterProfile.photos.value;
What should I be setting this to?
Just a thought, isn't twitterProfile.photos an array? probably you should try accessing twitterProfile.photos[0].value