User's relationship collection emptied afted user was updated - mongodb

I have two collections: users and tradeOffers. Each user has a unique steamid field. Each tradeOffer has field recipient.steamid. On server I publish tradeOffers collection like this:
import { Meteor } from 'meteor/meteor';
import { TradeOffers } from './collection';
if (Meteor.isServer) {
Meteor.publish("tradeOffers", function () {
let user = Meteor.users.findOne({_id: this.userId });
return TradeOffers.find({
'recipient.steamid': user.services.steam.steamid
});
});
}
On client side I subscribe to this collection and display current user's trade offers. Everything works fine until I update the user. Whenever current user is updated, data disappears from the view. After page reload I can see trade offers again. Any help would be appreciated.

Related

Meteor check whether document already exists in collection or not?

I am beginner in meteor. I have a form having username and password as input fields and a submit button in the end.
I have correctly collected data from both fields into two variables. Now what I want is to verify whether any matching document exists in my MongoDB collection or not? My below code is not working. How to do it? Please help. Here is my code.
Template.form.events({
'submit.login':function(event){
event.preventDefault();
var user = document.getElementById("myForm").elements[0].value;;
var pass = document.getElementById("myForm").elements[1].value;
var usernamee = (Collection.Login.find({username: user},{password: pass})).count();
if(usernamee>0) {
alert("found");
} else {
alert("not found");
}
return false;
}
});
Firstly your .find() is incorrect:
var usernamee = (Collection.Login.find({username: user},{password: pass})).count();
shoud be:
var usernamee = (Collection.Login.find({username: user, password: pass})).count();
Assuming that you're publishing that collection to the client either with autopublish or an explicit publication.
However:
You are giving even non-logged in users access to the usernames and cleartext passwords of all other users!
Meteor includes the accounts package that takes care of user management for you. You don't need to reinvent the wheel. You want to take advantage of the security work that's already been done for you.
You can use a method call to find out if a username has already been used and warn the new user in the UI before they create their account.
client:
Meteor.call('usernameExists', username, function(err, result){
if (result) {
alert('Username '+username+' is already taken!')
// clear out the form etc...
}
});
server:
Meteor.methods({
usernameExists(username){
return Meteor.users.findOne({username}) !== 'undefined';
}
});

Apollo Normalization with dataIdFromObject not updating

I am having trouble with getting React+Apollo to update the store after I send a delete mutation. I am using the reactQL boiler plate which has apollo+react built in and an express graphQL server (I didn't install the apollo server- I just use the reference express-graphQL package). My data is stored in the mongoDB with a _id, but the actual data on the client side uses id as the id.
The apollo client is defined like this:
new ApolloClient(
Object.assign(
{
reduxRootSelector: state => state.apollo,
dataIdFromObject: o => o.id
},
opt
)
);
I have a parent component which uses
import courses from 'src/graphql/queries/courses.gql
#graphql(courses)
export default class CoursesPage extends Component {
constructor(props){
super(props)
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(event) {
this.props.mutate({ variables: {id: selectedId}}
.catch(err => console.log(err))
}
render() {
return (
{ this.props.data.courses.map(course =>
<CourseDelete selectedId={course.id} key={course.id} />
})
}
)
}
}
and a child component that looks like:
import deleteCoursefrom 'src/graphql/mutations/deleteCourse.gql
#graphql(deleteCourse)
export default class CourseDelete extends Component {
constructor(props){
super(props)
this.handleDelete = this.handleDelete.bind(this);
}
handleDelete(event) {
this.props.mutate({ variables: {id: this.props.selectedId}}
.catch(err => console.log(err))
}
render() {
return (
<button onClick={this.handleDelete}>Button</button>
)
}
}
where deleteCourse.gql:
mutation deleteCourse($id: String!) {
deleteCourse(id: $id) {
id
}
}
and my original query is in courses.gql:
query courses {
courses {
id
}
}
Apollo's dataIdFromObject is used to update objects already in the cache. So if you have a record and an ID, and you change other pieces of data against that same ID, React components listening to the store can can re-render.
Since your deleteCourse mutation seems to return the same ID, it still exists in the cache. Your store doesn't know it needs deleting- it just updates the cache with whatever data comes back. Since this mutation likely returns the same ID, there's nothing to signify that this should be removed.
Instead, you need to specify an update function (link goes to the official Apollo docs) to explicitly delete the underlying store data.
In my new ReactQL user auth example, I do the same thing (see the pertinent LOC here) to 'manually' update the store after a user logs in.
Since components are initially listening to a 'blank' user, I cannot rely on dataObjectFromId to invalidate the cache, since I'm starting with no users and therefore no IDs. So I explicitly overwrite store state manually, which triggers re-rendering of any listening components.
I explain the concept is the context of the above user auth in a YouTube video - this is the piece that's relevant: https://youtu.be/s1p4R4rzWUs?t=21m12s

Add array object to minimongo

I have a chat app, that is using Ionic 2 and Meteor with MongoDB. It works perfectly.
However, everything is stored in the MongoDB on the server, so each time a user wants to view their messages, they need to be connected to the Meteor/Mongo Server running in the cloud. Also, if one user deletes their chat, it will delete the chat on the MongoDB, and the corresponding other user will also have their chat deleted.
I would like similar functionality as WhatsApp where the messages are held locally on the device (I am using SQLite), and only new messages are held in the cloud until both users download them.
Currently my app iterates over a Mongo.Cursor<Chat> object. It also observes this object (this.chats.observe({changed: (newChat, oldChat) => this.disposeChat(oldChat), removed: (chat) => this.disposeChat(chat)});).
I get chat data from SQLlite that I have stored locally (Array<Chat>).
Question
Is it possible to add the SQLite data (Array<Chat>) to the Mongo.Cursor<Chat>? When I do so, I want to just add to minimongo and not MongoDB on the server.
Thanks
UPDATE
Asp per advise below, I do the following:
let promise: Promise<Mongo.Cursor<Chat>> = new Promise<Mongo.Cursor<Chat>>(resolve => {
this.subscribe('chats', this.senderId, registeredIds, () => {
let chats: Mongo.Cursor<Chat> = Chats.find(
{ memberIds: { $in: registeredIds } },
{
sort: { lastMessageCreatedAt: -1 },
transform: this.transformChat.bind(this),
fields: { memberIds: 1, lastMessageCreatedAt: 1 }
}
);
this.localChatCollection = new Mongo.Collection<Chat>(null);
console.log(this.localChatCollection);
chats.forEach(function (chat: Chat) {
console.log('findChats(): add chat to collection: ' + chat);
this.localChatCollection.insert(chat);
});
Will update if it works.
UPDATE
When I do the following, it inserts the chat object:
let promise: Promise<Mongo.Collection<Chat>> = this.findChats();
promise.then((data: Mongo.Collection<Chat>) => {
let localChatCollection: Mongo.Collection<Chat> = new Mongo.Collection<Chat>(null);
data.find().forEach(function (chat: Chat) {
console.log('==> ' + chat);
localChatCollection.insert(chat);
});
However, if I define the localChatCollection globally, it does not insert the chat object. There are no errors but the process just stops on the insert line.
private localChatCollection: Mongo.Collection<Chat> = new Mongo.Collection<Chat>(null);
....
this.localChatCollection.insert(chat);
Any ideas how I can get this to insert into a globally defined collection?
Is it possible to add the SQLite data (Array) to the Mongo.Cursor? When I do so, I want to just add to minimongo and not MongoDB on the server.
Meteor itself knows nothing about SQLite, but it sounds like you have that part of it working.
To just add to minimongo and not the mongodb server, you're looking for a client-side collection. Just pass null in as the first parameter to the call to create your collection i.e.
var localChatCollection = new Mongo.Collection(null)
You can then insert to localChatCollection the same way you would with a synchronized collection.
Source: Meteor docs

Accessing services field of Meteor.users

When I query Meteor.users I do not receive the services field or any other custom fields I have created outside of profile. Why is it that I only receive _id and profile on the client and how can I receive the entire Meteor.users object?
Thanks.
From the DOcs
By default, the current user's username, emails and profile are published to the client. You can publish additional fields for the current user with:
As said above If you want other fields you need to publish them
// server
Meteor.publish("userData", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'services': 1, 'others': 1}});
} else {
this.ready();
}
});
// client
Meteor.subscribe("userData");
The above answer does work, but it means you have to subscribe to said data, which you should do if you are getting data from users other than the currently logged in one.
But if all you care about is the logged in user's data, then you can instead use a null publication to get the data without subscribing.
On the server do,
Meteor.publish(null, function () {
if (! this.userId) {
return null;
}
return Meteor.users.find(this.userId, {
fields: {
services: 1,
profile: 1,
roles: 1,
username: 1,
},
});
});
And this is actually what the accounts package does under the hood

Mongoose - update after populate (Cast Exception)

I am not able to update my mongoose schema because of a CastERror, which makes sence, but I dont know how to solve it.
Trip Schema:
var TripSchema = new Schema({
name: String,
_users: [{type: Schema.Types.ObjectId, ref: 'User'}]
});
User Schema:
var UserSchema = new Schema({
name: String,
email: String,
});
in my html page i render a trip with the possibility to add new users to this trip, I retrieve the data by calling the findById method on the Schema:
exports.readById = function (request, result) {
Trip.findById(request.params.tripId).populate('_users').exec(function (error, trip) {
if (error) {
console.log('error getting trips');
} else {
console.log('found single trip: ' + trip);
result.json(trip);
}
})
};
this works find. In my ui i can add new users to the trip, here is the code:
var user = new UserService();
user.email = $scope.newMail;
user.$save(function(response){
trip._users.push(user._id);
trip.$update(function (response) {
console.log('OK - user ' + user.email + ' was linked to trip ' + trip.name);
// call for the updated document in database
this.readOne();
})
};
The Problem is that when I update my Schema the existing users in trip are populated, means stored as objects not id on the trip, the new user is stored as ObjectId in trip.
How can I make sure the populated users go back to ObjectId before I update? otherwise the update will fail with a CastError.
see here for error
I've been searching around for a graceful way to handle this without finding a satisfactory solution, or at least one I feel confident is what the mongoosejs folks had in mind when using populate. Nonetheless, here's the route I took:
First, I tried to separate adding to the list from saving. So in your example, move trip._users.push(user._id); out of the $save function. I put actions like this on the client side of things, since I want the UI to show the changes before I persist them.
Second, when adding the user, I kept working with the populated model -- that is, I don't push(user._id) but instead add the full user: push(user). This keeps the _users list consistent, since the ids of other users have already been replaced with their corresponding objects during population.
So now you should be working with a consistent list of populated users. In the server code, just before calling $update, I replace trip._users with a list of ObjectIds. In other words, "un-populate" _users:
user_ids = []
for (var i in trip._users){
/* it might be a good idea to do more validation here if you like, to make
* sure you don't have any naked userIds in this array already, as you would
*/in your original code.
user_ids.push(trip._users[i]._id);
}
trip._users = user_ids;
trip.$update(....
As I read through your example code again, it looks like the user you are adding to the trip might be a new user? I'm not sure if that's just a relic of your simplification for question purposes, but if not, you'll need to save the user first so mongo can assign an ObjectId before you can save the trip.
I have written an function which accepts an array, and in callback returns with an array of ObjectId. To do it asynchronously in NodeJS, I am using async.js. The function is like:
let converter = function(array, callback) {
let idArray;
async.each(array, function(item, itemCallback) {
idArray.push(item._id);
itemCallback();
}, function(err) {
callback(idArray);
})
};
This works totally fine with me, and I hope should work with you as well