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();
});
});
Related
I'm currently implementing admin dashboard of online shopping app.I want to implement method to perform user deletion and store that deleted user data temporally on another collection.
(Copy_userdata->Save it on another collection -> delete original data)
As an example my users data currently available in collection called users, and after deleting that user particular user's data must be available in another collection, lets say deleted_users collection. Are there any easy way to do that? thanks!
You will be modify some of the code but this is the basic logic,
Use aggregation for copy collections over
Refer here for aggregate function using mongo client
So the function looks like this
public aggregation(collectionName: string, pipelines: Object[]): Promise<Array<any>>
{
return new Promise((resolve, reject) =>
{
let cursor: mongodb.AggregationCursor<any> = null;
//Here you will use getCollection method on your own to fetch the collection
this.getCollection(collectionName)
.then((collection: mongodb.Collection) =>
{
cursor = collection.aggregate(pipelines);
return cursor.toArray();
})
.then((result: Array<any>) =>
{
return resolve(result);
})
.catch((error: any) =>
{
//error//
});
}
public dropCollection(collectionName: string): Promise<any>
{
return new Promise((resolve, reject) =>
{
this.getCollection(collectionName)
.then((collection: mongodb.Collection) =>
{
collection.drop((err: Error, result: any) =>
{
if (err)
{
return reject(DataDropError);
}
return resolve(result);
});
})
.catch(reject);
});
}
public async backupAndDrop()
{
const Object = [ { $match: {} }, { $out: "DeletedCollection" } ];
try
{
await this.aggregationPipeline("originalCollection", Object);
await this.dropCollection("originalCollection");
}
catch (e)
{
throw e;
}
}
Also try to run this on your mongo shell:
db.originalCollection.aggregate([ { $match: {} }, { $out: "Backup" } ])
Why don't you add a flag like isDeleted which is false by default and then make it true when the user is deleted?
You can do something like this...
Client.connect(connection_string, function(err, db) {
if(err){
console.log(err);
}
else{
db.collection(CollectionA).find().forEach(function(d){ db.collection(CollectionB).insert(d); });
}
Try out if it works.
This can help too:
How to properly reuse connection to Mongodb across NodeJs application and modules
You can first find the record to be deleted and do a create with that data to the new collection and then delete the record.
db.collection(CollectionA).findOne({userIdTODelete}, function(err, res){
db.collection(CollectionB).insertOne(res, function() {
db.collection(CollectionA).deleteOne({userIdTODelete});
})
});
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 have some problem with transactions in ArangoDB+nodejs. I need to do something like this:
transaction
{
insertedId=insertItemOneInDB();
insertItemTwoInDB();
}
but when the second insert failed, the first one didn't rollback!
please help me with an example!
here is my code:
var transaction = function (collections,params,callback)
{
try
{
db.transaction.submit("user_merchant_tbl",params,
function ()
{
console.log("_collections:",collections);
console.log("params:");
console.log(params);
console.log("---");
console.log("+==========+");
//
var insertedDataId;
var relationsArrayIds=[];
db.document.create(collections,params.data).then(function(_insertedId)
{
insertedDataId=_insertedId;
}
,function(err)
{
console.log("ERROR: Arango--insert-->err: %j", err);
//throw "Error: "+err;
return false;
});
/////
var relations=params.relations;
for(var i=0;i<relations.length;i++)
{
db.document.create(relations[i].edge,relations[i].data).then(
function(_id)
{
relationsArrayIds.push(_id);
next(true);
}
,function(err)
{
console.log("ERROR: Arango--insert.edge-->err:23232 %j", err);
console.log("after return");
next(false);
return false
});
}
console.log("transaction before true");
function next(result)
{
if(result==true)
{
console.log("transaction is ok:",result);
callback(insertedDataId,result);
}
else
{
console.log("transaction is not OK:",result);
callback(insertedDataId,false);
}
}
}
);
}
catch(e)
{
console.log("catch->error in -->Arango.transaction: ",e);
}
}
first of all there seems to be a misunderstanding in how to write the action that is supposed to be executed. This action is executed directly on the Database Server , hence you cant use any functionality provided by the Arango Javascript api.
If you want to design your action it has to run in the arango shell or on the server console (bin/arangod data --console)
I took a look into your code and assume you want to store relations between users and merchants. As Arango comes with a nice graph module you could follow the following approach :
// First we define a graph, containing of 2 document collections ("users" and "merchants") and 2 edge collections (one per relation type, in this example "contactRequested" and "boughtSomethingFrom".
// Note that in this definition the relation "boughtSomethingFrom" is only allowed from a user to a merchant. Of course this is just one way to design it, you have to do it the way it suits you the best.
var edgeDefinitions = [{
collection: "contactRequested",
from: ["users", "merchants"],
to: ["users", "merchants"]
}, {
collection: "boughtSomethingFrom",
from: ["users"],
to: ["merchants"]
}];
// Now we create a graph called "user_merchant_graph" and in the callback function execute a transaction
db.graph.create("user_merchant_graph", edgeDefinitions, function(err, ret, message) {
// Lets define the action for the transaction, again this will be executed directly on the server ......
var action = function (params) {
// We have to require the database module ....
var db = require("internal").db;
var relationsArrayIds = [];
// now we store the user provided to the function
var insertedUserId = db["users"].insert(params.data)._id;
var relations = params.relations;
// Now we loop over through the relations object, store each merchant and it's relations to the user
Object.keys(relations).forEach(function (relation) {
// store merchant
var insertedMerchantId = db["merchants"].insert({merchantName : relation})._id;
// store relation as edge from "insertedUserId" to "insertedMerchantId".
var edgeId = db[relations[relation].relation].insert(insertedUserId, insertedMerchantId, relations[relation].additionalData)._id;
relationsArrayIds.push(edgeId);
});
};
// End of action
var options = {};
options.params = {
data: {
userName : "someUserName",
userSurname : "someUserSurname"
},
relations : {
merchantA : {relation : "contactRequested", additionalData : {data :"someData"}},
merchantB : {relation : "boughtSomethingFrom", additionalData : {data :"someData"}},
merchantC : {relation : "contactRequested", additionalData : {data :"someData"}}
}
};
// Now we call the transaction module ... a note to the collections parameter, it has to be an object containing the keys "write" and "read" which have a list of all collections as value into which the action is writing /reading from
// This collections object is NOT available within your action, the only thing passed as argument to your action is "options.params" !!
db.transaction.submit({write : ["users", "merchants", "contactRequested", "boughtSomethingFrom"]}, action, options, function(err, ret, message) {
//some callback
});
});
With regards to transactions they are working, you can give this code a shot and if you f.e. mess up the storing of the edges (change it to "var edgeId = db[relations[relation].relation].insert(relations[relation].additionalData)._id;")
you will see that your user and merchant have not been stored
I hope this helps
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 ?
I'm designing a client/server synchronization feature. The client sends a bunch of changed events to server. The server will do creation, deletion or modification upon requested item status. After the database operation, the server need send a summary back to client.
Below is excerpt from my server side code, designed with mongoose and restify.
var EventModel = mongoose.model('Event', eventSchema);
server.post("/sync", function (req, res, next) {
var events = req.params.events;
var created = [], deleted = [], updated = [];
events.forEach(function (elem) {
if (elem.status == 0) {
// Delete
EventModel.remove({ _id: elem.uuid }, function (err, event) {
if (!err) deleted.push({uuid: elem.uuid});
});
} else if (elem.status == 1) {
// Create and update uuid
var event = new EventModel(elem);
event.save(function (err, doc) {
if (!err) {
elem.uuid = event._doc._id;
created.push(elem);
}
});
} else if (elem.status == 2) {
// Update
EventModel.findOne({ _id: elem.uuid }, function (err, event) {
event.save(function (err, doc) {
if (!err) updated.push({uuid:elem.uuid});
});
});
}
});
// Notify client what are processed.
// PROBLEM: created, deleted, updated are always empty!
res.send({processed: {created: created, deleted: deleted, updated: updated}});
});
Since mongoose do CRUD in async way, the response created,deleted and updated are always empty.
Is there any way to let the mongoose operation in series?
As stated in the comments you could use the npm async module.
Alternatively, you may prefer to nest callbacks (but this might lead to what is known as callback hell, viz many nested callbacks) or take advantage of the mongoose .then() method - see http://mongoosejs.com/docs/promises.html
Here you can do ..
EventModel.remove(args).then((removeResponse) => {
return EventModel.findOne(args);
}).then((findResponse) => {
// etc
})
These requests will happen synchronously.