I am developing a referral system, in which I will increase invite counter of inviter.
I did a lot search but didn't find working example.
Bellow is the code
app.get('/increase-counter', function( request, response ) {
//Sdk Link: http://blog.parse.com/2012/10/11/the-javascript-sdk-in-node-js/
var Parse = require('parse-cloud').Parse;
Parse.initialize( config.parse.appId, config.parse.jsKey );
Parse.Cloud.define("modifyUser", function(request, response) {
console.log('hello4');
if (!request.user) {
response.error("Must be signed in to call this Cloud Function.")
console.log('hello5');
return;
}
// The user making this request is available in request.user
// Make sure to first check if this user is authorized to perform this change.
// One way of doing so is to query an Admin role and check if the user belongs to that Role.
// Replace !authorized with whatever check you decide to implement.
if (!authorized) {
console.log('hello6');
response.error("Not an Admin.")
return;
}
// The rest of the function operates on the assumption that request.user is *authorized*
Parse.Cloud.useMasterKey();
// Query for the user to be modified by username
// The username is passed to the Cloud Function in a
// key named "username". You can search by email or
// user id instead depending on your use case.
var query = new Parse.Query(Parse.User);
//query.equalTo("username", request.params.username);
query.equalTo("objectId", req.query.objectId);
console.log('hello7');
// Get the first user which matches the above constraints.
query.first({
success: function(anotherUser) {
console.log('hello8',anotherUser);
// Successfully retrieved the user.
// Modify any parameters as you see fit.
// You can use request.params to pass specific
// keys and values you might want to change about
// this user.
anotherUser.set("inviteCounter", 2);
// Save the user.
anotherUser.save(null, {
success: function(anotherUser) {
// The user was saved successfully.
response.success("Successfully updated user.",anotherUser);
},
error: function(gameScore, error) {
// The save failed.
// error is a Parse.Error with an error code and description.
response.error("Could not save changes to user.",gameScore,error);
}
});
},
error: function(error) {
response.error("Could not find user.",error);
}
});
});
console.log('hello1');
Parse.Cloud.run('modifyUser', { objectId: request.query.objectId }, {
success: function(status) {
console.log('hello2');
// the user was updated successfully
},
error: function(error) {
console.log('hello3', error);
// error
}
});
});
When I run url, its showing result in terminal:
hello1
hello3 { code: 141, message: 'function not found' }
Please let me know how could I update the inviteCounter ?
Related
I have a function that should update a token, based on an user's email. The thing is, the following code returns a token even if there isn't any document with the specified email in the mongoDB database and the function return the response code 200 to my server function. I'd like to prevent the updating of the document (and any further actions) when the specified e-mail isn't in the database or i'd like to return some information (regardless of the response code) to prevent further code from executing.
const vnosZetona = (req,res) =>{
if(!req.body.ePosta ){
return res.status(400).json({
"sporočilo": "Epošta uporabnika manjka! Parameter je obvezen"
});
}
if(!(new RegExp("[a-z]{2}[0-9]{4}#student.uni-lj.si").test(req.body.ePosta))){
return res.status(400).json({
"sporočilo": "Izgleda da nisi študent UL! Hm, "
});
}
var generiranZeton = generirajObnovitveniZeton();
User
.updateOne( {email: req.body.ePosta},
{ $set: {zetonZaObnavljanjeGesla:generiranZeton}},
(napaka) => {
if(napaka){
return res.status(400).json(napaka);
}else{
return res.status(200).json({
zeton : generiranZeton,
"sporočilo" : "Žeton uspešno dodan."
});
}
}
)
};
So after a series of trials & errors I finally figured out what's really wrong. When I issued the query (whith an email which was not in any document) direcly into the mongoDB shell I got the following response: { "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0 }
The result clearly says that the wasn't any update ( modifiedCount is 0) but the query was still executing without any errors, so I had to "collect" that text and then continue the execution based on the "modifiedCount" value.
const vnosZetona = (req,res) =>{
//check for errors and other stuff
var generiranZeton = generirajObnovitveniZeton(); //generate random token
User
.updateOne( {email: req.body.ePosta},
{ $set: {zetonZaObnavljanjeGesla:generiranZeton}},
(napaka, sporociloQueryja) => {
if(napaka){
return res.status(400).json(napaka);
}else{
return res.status(200).json({
zeton : generiranZeton,
status: JSON.stringify(sporociloQueryja),
//the field "status" returns the result mentioned above
//although slightly different than in the mongoDB shell:
//{"n":0,"nModified":0,"ok":1}
"sporočilo" : "Žeton uspešno dodan."
});
}
}
);
};
//The following code calls the API above when we complete the form on the page and hit submit
const posljiZahtevoZaObnovoGesla = async (req,res,next) =>{
//check for a valid email address
try{
let odgovor = await axios.put(apiParametri.streznik + "/api/uporabniki/vnosZetona",{
ePosta : req.body.ePosta
});
if(odgovor.status == 200){
//If the query had no errors,regardless if anything was updated
// we read the data that was returned from the API ->
// with a nonexistent mail, the "odgovor.data.status" equals "{"n":0,"nModified":0,"ok":1}"
var o = JSON.parse(odgovor.data.status);
if(o.nModified == 0){
//nothing was modified, the no document with the specified email exists
// the user gets redirected to registration form
return res.redirect("/registracija");
}
//the code sends the instructions to the specified mail that exists in database
//using nodemailer and redirects to user sign in
res.redirect("/prijava");
}
else if (odgovor.status >= 400){
res.status(odgovor.status).json(
{"sporocilo": "Nekaj si zafrknil! Rollbackaj na začetno stanje!"}
);
}
}catch(napaka){
next(napaka);
}
};
I have a website that offers a simple messaging service. Individuals can pay for the service, or a business can pay for a monthly subscription and then add their clients/users for free. When the business adds a client/user email, that triggers the function below. I'm using firebase functions and createUser to create the user on my server(less). However, sometimes a business tries to register a user and that user already exist. In this case, I want to send the user a reminder email.
The code I have works fine, but it feels funky having a chain within my catch/error. Is there another way to detect if an email is already registered with a Firebase account that won't throw an error?
exports.newUserRegisteredByBusiness = functions.database.ref('users/{uid}/users/invited/{shortEmail}').onWrite( (data, context) => {
//don't run function if data is null
if (!data.after.val()){
console.log('SKIP: newUserRegisteredByBusiness null so skipping')
return null
} else {
let businessUID = context.params.uid
let email = data.after.val()
let shortEmail = context.params.shortEmail
let password // = something I randomly generate
return admin.auth().createUser({ email: email, password: password}).then( (user)=> {
//write new user data
let updates = {}
let userData // = stuff I need for service to run
updates['users/' + user.uid ] = userData;
return admin.database().ref().update(updates)
}).then( () =>{
//email new user about their new account
return emailFunctions.newUserRegisteredByBusiness(email, password)
}).catch( (error) =>{
//if user already exist we will get error here.
if (error.code === 'auth/email-already-exists'){
//email and remind user about account
return emailFunctions.remindUsersAccountWasCreated(email).then( ()=> {
//Once email sends, delete the rtbd invite value that triggered this whole function
//THIS IS WHERE MY CODE FEELS FUNKY! Is it ok to have this chain?
return admin.database().ref('users/' + businessUID + '/users/invited/' + shortEmail).set(null)
})
} else {
//delete the rtbd value that triggered this whole function
return admin.database().ref('users/' + businessUID + '/users/invited/' + shortEmail).set(null)
}
});
}
})
To find if a user account was already created for a given email address, you call admin.auth().getUserByEmail.
admin.auth().getUserByEmail(email).then(user => {
// User already exists
}).catch(err => {
if (err.code === 'auth/user-not-found') {
// User doesn't exist yet, create it...
}
})
While you're still using a catch() it feels like a much less failed operation.
To avoid further implementation in the catch block you can wrap this Firebase function into this code:
async function checkUserInFirebase(email) {
return new Promise((resolve) => {
admin.auth().getUserByEmail(email)
.then((user) => {
resolve({ isError: false, doesExist: true, user });
})
.catch((err) => {
resolve({ isError: true, err });
});
});
}
...
const rFirebase = await checkUserInFirebase('abc#gmail.com');
I'm creating a RESTful API.
I wanna use GET method to check if lastName exists. If it can find lastName, return "YES", otherwise, call a POST method to create a data with lastName entered.
The problem is that it can create a new data, but the body is empty. Ideally, it should contain a value with lastName, like "lastName": "James",
{
"_id": "58a22c3c3f07b1fc455333a5",
"__v": 0
}
Here is my code.
router.route("/findLastName/:id")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.id}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
res.json(response);
}
if (result) {
response = "YES";
res.send(response);
} else {
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
var POSTurl = "http://localhost:6002/users";
var params = "lastName=" + req.params.id;
xhr.open("POST", POSTurl, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(params);
}
});
})
PS: GET method works well, not a issue.
Let me modify a bit of your code and add comments as pointers:
// changed findLastName to find-last-name. It's a common convention,
// urls need to be case insensitive. It doesn't concern lastName, as
// that's a parameter, internal to your app so it's fine.
// even better if you name the route `find-or-create` or something, to better
// reflect what you're doing.
router.route("/find-last-name/:lastName")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.lastName}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
// Adding a `return statement here. If you don't return, you'll tell
// the user that there was an error, but your code continues running
// potentially calling that res.json twice.
// Also, since it's an internal error, it's common to tell the client
// about it, by setting the status to 500
return res.status(500).json(response);
}
if (result) {
// turning the message to JSON instead. You started that above,
// and for the sake of your clients (your frontend), it's
// better to stick to JSON. Also you can pass useful info, such as
// _id of the document.
// Again adding a `return` here, and then the rest of the code
// is nested one level less. not required, but some people like that.
response = {
message: "Last name exists."
};
return res.json(response);
}
// Here begins the new code. I'm typing what I can infer from your code,
// I don't know if your MongoDB driver looks like that exactly.
mongoOp.insert({
deviceId: req.params.lastName
// add other optional properties here.
}, function (err, response) {
if (err) {
var message = {
error: true,
message: 'Cannot save new entry.'
}
return res.status(500).json(message);
}
// if we're here, everything went ok. You can probably return
// the _id of the given user.
return res.json({
message: 'New user created.',
_id: response._id
});
});
});
})
Is it possible to reuse / call the blueprint function (find/create/update/destory) and just add some items needed for the controllers. I'm sorry if I'm having hard time expressing my question but hopefully my example will help.
Example:
modue.exports = function(){
index: ....,
create: function(req, res){
try{
// i want to call the blueprint here to save some things
create(req, res);
// then do more after creating the record
....
}catch(err){
// want to catch some error here like validation err
// instead of sending it to res.serverErr();
}
}
....
}
//File api/controller/UserController.js
// suppose the model is User under api/models
modue.exports = {
create: function(req,res){
// pass req.query to User Model's Create function, so you dont need to rewrite all
//optional paramters for this overwrite version
User.create(req.query).exec(function(e, r){
if(e){
// !!!try to create in a different way!
}
})
}
}
You need to first copy blueprint folder from sails which is present in node_modules folder
Paste the blueprint folder in you api folder
Then in your controller for e.g UserController include actionUtil for e.g
var actionUtil = require('sails/lib/hooks/blueprints/actionUtil');
module.exports = {
create: function (req, res) {
// paste code from blueprint create.js file
var Model = actionUtil.parseModel(req);
// Create data object (monolithic combination of all parameters)
// Omit the blacklisted params (like JSONP callback param, etc.)
var data = actionUtil.parseValues(req);
// Create new instance of model using data from params
Model.create(data).exec(function created(err, newInstance) {
// Differentiate between waterline-originated validation errors
// and serious underlying issues. Respond with badRequest if a
// validation error is encountered, w/ validation info.
if (err)
return res.serverError({status:500, message:'error', err: err});
// If we have the pubsub hook, use the model class's publish method
// to notify all subscribers about the created item
if (req._sails.hooks.pubsub) {
if (req.isSocket) {
Model.subscribe(req, newInstance);
Model.introduce(newInstance);
}
// Make sure data is JSON-serializable before publishing
var publishData = _.isArray(newInstance) ?
_.map(newInstance, function (instance) {
return instance.toJSON();
}) :
newInstance.toJSON();
Model.publishCreate(publishData, !req.options.mirror && req);
}
// do your after create stuff here
// Send JSONP-friendly response if it's supported
res.ok({status: 200, message: 'ok', results: newInstance});
});
}
}
I have a list of users. I don't want to publish all user data to the client, especially emails. I have multiple publish methods where i can use:
Meteor.publish('usersData', function() {
return Users.find({}, {
fields: {
emails: 0
}
});
});
But what if I or other programmer forget to filter fields and just publish whole collection:
Meteor.publish('users', function() {
return Users.find();
});
It's a problem. There should be global settings to filter data in collection. Is there any way how to do it in current (0.6.6.3) Meteor?
You can create a method you use instead of the normal collection.find method that you use anywhere you need to publish users. An example could be:
function findUsers(query) {
return Meteor.users.find(query || {}, { fields: { emails: 0 } });
}
And then you can just remind your programmers to use the findUsers method:
Meteor.publish('userData', function () {
return findUsers({ points: { $gt: 5 } });
});
How about writing a collection observer that throws an exception whenever a user with email fields present was published.
The observer runs independently for each connected user and triggers every time a user object has been pushed to the user collection. If it is not the current user, throw an error if the object contains the email field.
Your team should then notice these exceptions during development.
Meteor.publish("userCheck", function () {
var self = this;
var handle = Meteor.users.find({}).observeChanges({
added: function(id) {
var user = Meteor.users.findOne({_id: id});
if (user.emails && self.userId !== id) {
throw new Meteor.Error(500, "Must not publish other people's email!");
}
}
});
self.ready();
self.onStop(function () {
handle.stop();
});
});