Dependency between branches in state tree in Redux - redux

I have a state object with the following branches (trying to adhere to "Normalizing the state shape"):
Users
An array of elements like
{
id: 1,
name: "Werner"
}
originating from some server.
User locations
An array of elements like
{
userId: 1,
latitude: 45,
longitude: 70
}
originating from some server.
The problem
The users might change depending on a number of actions: SET_USERS_ACTION, ADD_USER_ACTION, DELETE_USER_ACTION.
Every time something happens to the users, I want to update the user locations (which is an asynchronous operation, as the data needs to come from the server). The how of the matter is what I'm struggling with.
Obviously, I can't fetch the user locations in the reducer (when updating the users), as the reducer would no longer be pure in that case.
I might do it in the thunk, but that would mean I have to add user location considerations to every action creator involving user-actions, which seems like mixing concerns to me.
Additionally, once an action is added that changes the users array in some way, the developer needs the remember to also update the user locations. My experience is that stuff like this will almost always be forgotten at some point.
Further complications
To further complicate the matter, we don't always need to fetch the locations. Only if a component displaying a map with all users is active, does it make sense to fetch the user locations. Not every action is generated at a place where I know (beforehand) if that component is visible or not. One example is when we receive a notification from the server (with Web Sockets) that a user was added or removed.
What is the best way of solving this problem?

I'll suggest to use https://github.com/kolodny/immutability-helper The benefit of using the update helper is that you are able to do many changes at once without touching the state many times. For example:
import update from 'immutability-helper';
...
case SET_USERS_ACTION:
return update(
state,
{
users: {
[idx]: { status: { $set: 'ready' }}
},
locations: {
$push: [{...}]
}
}
);
break;

Related

Where to keep "currentUser" globally accessible

Almost every object in my state tree needs to have the following fields:
createdByGuid
createdAt
modifiedAt
The date fields are easy to add in the action creators. There seems to be consensus that action creators are the place where impureness are allowed, i.e. new Date().
But the currently logged in user is not easily fetched since this data lives in another slice of the state.
Example state:
profile
data
guid
firstname
...
media
items
0: guid, createdByGuid, ...
1: guid, createdByGuid, ...
drawings
items
...
When a MEDIA_ADD action is dispatched I want the action creator to augment the dispatched data with createdById. Current user needs to be fetched from profile.data.guid.
I don't want to bring the state into my action creator. Instead I can use a thunk to get hold of the state via getState(). But this seems to be a clumsy way of getting hold of a simple guid constant (constant for this session). It becomes more clumsy when essentially all of my action creators for adding data to the state needs to be thunks.
I'm inclined to create a separate global singleton object to hold this data and bring this dependency into all of my action creator modules. This seems to be a lot less disturbing dependency in terms of for example testing.
I suppose most Redux users has about the same question regarding current user. What is a good solution?
I'm working in react-native, but I suppose the question applies to most Redux applications.
If someone is interested, I went the singleton way with this. Not a true singleton per definition, but it suits my needs.
When a user logs in I handle this in an asynchronous action creator (a thunk). The thunk calls setGlobalCurrentUser() after successfully authenticating the user with the server.
To get hold of the current user I only have to import currentUserGuid() from currentUser.js and I don't end up in a dependency hell.
currentUser.js:
let _currentUserGuid;
export function currentUserGuid() {
if(!_currentUserGuid) throw new Error('currentUserGuid:: No logged in user')
return _currentUserGuid;
}
export function setGlobalCurrentUser(user) {
if(user) {
_currentUserGuid = user.get('guid')
} else {
_currentUserGuid = null
}
}

How to Reproduce Meteor.user() Client-Server effect for a different Collection?

Just how Meteor.user() method is available on Client & Server for the "current user" I would love to reproduce this kind of functionality for different custom collections. For example, my app uses a "clouds" collection as a type of room for a group of users to be in. Obviously there are various cloud instances and I don't always want to be passing the cloudId into every single meteor method. Ideally I could have like a Meteor.cloud() function that would give me the current cloud on the client and server.
My thoughts on approaching this:
What I have been doing thus far is piggy-backing off of Meteor.user() by storing a currentCloudId property inside the user profile and setting that on a route beforeAction. However this limits the user to only being in 1 cloud at a time.
Using the Meteor.connection ID somehow to keep a map of connectionIds to cloudIds. This would work great in theory....however it seems that Meteor connection IDs cannot be heavily relied on as they might change during reconnects or other random scenarios. Also you would have to then "manange" that collection of "cloudConnections" and remove old stale ones and such.
Im using Iron Router....and if it were possible to get the current route data on the server that would also solve my problem but I am not sure how to access that on the server?
--- Basically I would love for a simple straight forward way to mimic Meteor.user() behavior for other collections.
Thanks for all your help :)
You can just create a function inside /lib that looks something like this:
getUserClouds = function getUserClouds () {
return Clouds.find({ $elemMatch: { $eq: Meteor.userId() } })
}
This will work both on the client and on the server. But it will always return a Cursor pointing to 0 docs. So you'll need a publication:
Meteor.publish('userClouds', function () {
return Clouds.find({ $elemMatch: { $eq: this.userId } })
})

How can I avoid an infinite loop in my meteor router?

I'm building an online store in meteor where customers can customize products in the store. I have setup a client-only collection called Inventory which stores all the product data and is updated accordingly in response to user input. Once the user is ready to checkout, I dump the product data into a client & server side collection called ShoppingCart. I want to allow users to go back and revise their edits on the product in Inventory so I setup my router to $set data from the ShoppingCart into Inventory if it finds a match:
Router.route '/:_type/:_id', ->
Session.set "inCart", false
#render #params._type,
data: =>
storedItem = ShoppingCart.findOne {
userId: Meteor.userId(),
image: #params._id
}
if storedItem?
delete storedItem._id
Inventory.update {image: #params._id}, {
$set: storedItem
}
Inventory.findOne image: #params._id
EDIT: This seems to cause my router method to get stuck in an infinite loop whenever data in Inventory changes. Is there any way to avoid this issue? Is there a better way of handling this kind of data altogether that I should consider?
MAJOR CAVEAT - I don't do CoffeeScript, so this is what I can gather having put your code through a compiler.
I think the problem is that the data function is reactive, and you're updating and returning an item from the Inventory collection within it. Every time the route runs, unless there is no storedItem, it's going to invalidate a computation on which it itself depends and thus rerun again immediately (and subsequently do the same again, etc...).
As a general rule, I think it's a very bad idea indeed to be updating a collection from within a data function - if you have to do this within the route function, consider the onRun, or onBeforeAction hooks for the update.
Final thing, just because I don't understand: why do you need to dump the item from the ShoppingCart back into Inventory? Shouldn't it already be there, unless the user has started a new session?

How to prevent collection modification via console for an otherwise secure update operation?

I am fairly new to Meteor and am just trying to figure out meteor security.
I am writing a quiz app that allows a logged in user to save their scores. I have created a collection which consists of a user id and an array of scores. The way I expose a push of new score is a method on the server side:
Meteor.methods({
'pushScore' : function(playerId, playerScore) {
UserScores.upsert({ userId : playerId}, {$push : {scores : playerScore}});
}
});
I call the method on click of a button from the client like so:
if (Meteor.userId()){
Meteor.call('pushScore', Meteor.userId(), Session.get("score"));
}
I have the following concerns here:
Obviously the user can manipulate the score value in "Session" and cheat the system. What could be an alternate secure mechanism to keep track of the running score while a quiz is being taken?
The other one is probably a bigger concern. How do I prevent the user from just firing a console call to my method "pushScore" and again cheat the system by adding, say a score of 100?
Is there an inherent flaw in the way I have designed here?
This is just a sample application, but I can easily imagine a real world scenario which could mimic this. What woudl be a best practice in such a scenario?
Thanks in advance.
Cheers..
As #Peppe suggested, you should move the logic to the server somehow. The main rule for Meteor security (and web security in general) is
You cannot trust the client.
The reason for that is what you've already mentioned: if there is something a client can do, then there is no way to stop a rogue user to do the same thing from the browser console, or even to write his own malicious client that will exploit the leak.
In your case, that means that if client is able to add points to scores, then the user is able to do so as well, regardless on what security measures you employ. You can make this more or less difficult, but your system has a designed leak which cannot be completely closed.
Thus, the only bulletproof solution is to make the server decide on when to assign points. I assume that in a quiz app user gets points when he choose the right answer to a question. So instead of checking that on the client, create a server–side method that will receive the question ID, answer ID, and increase user scores if the answer is correct. Then make sure user cannot just call this method with all possible answer, with a way that corresponds to your quiz design – for example give negative points if wrong answer is chosen, or allow to answer the same question only once in a period of time.
Finally, make sure the client doesn't just get the correct answer ID in the data it receives.
In a nutshell, there are 2 common soloutions to your problem:
if you're using a Meteor.method dont pass any arguments in the Meteor.call, the server can and should gather the data it plans to insert/update on the server side.
you can add a validation function to the collection using the collection "allow" method to verify any updates from the client, in that case you don't need the Meteor.method and can just update from the client and validate it server-side.
Security (insert/update/delete operations) in meteor works in the same way as security in any other framework: before executing an action taken by the user, make sure the user has the rights to perform it. Security may appear as a weakness in Meteor, but it does not suffer from it any more than other frameworks (though, it's easier to exploit it in Meteor through the console).
The best way to solve it probably varies from case to case, but here's an example: if a user posts a post, the user should gain 5 points. Here's a bad way to solve it:
if(Meteor.isClient){
// Insert the post and increase points.
Posts.insert({userId: Meteor.userId(), post: "The post."})
Meteor.users.update(Meteor.userId(), {$inc: {'profile.points': 5}})
}
if(Meteor.isServer){
Posts.allow({
insert: function(userId, doc){
check(doc, {
_id: String,
userId: String,
post: String
})
// You must be yourself.
if(doc.userId != userId){
return false
}
return true
}
})
Meteor.users.allow({
update: function(userId, doc, fieldNames, modifier){
check(modifier, {
$inc: {
'profile.points': Number
}
})
if(modifier.$inc['profile.points'] != 5){
return false
}
return true
}
})
}
What makes it bad? The user can increase his points without posting a post. Here's a better solution:
if(Meteor.isClient){
// Insert the post and increase points.
Method.call('postAndIncrease', {userId: Meteor.userId(), post: "The post."})
}
if(Meteor.isServer){
Meteor.methods({
postAndIncrease: function(post){
check(post, {
userId: String,
post: String
})
// You must be yourself.
if(post.userId != this.userId){
return false
}
Posts.insert(post)
Meteor.users.update(this.userId, {$inc: {'profile.points': 5}})
}
})
}
Better, but still bad. Why? Because of the latency (the post is created on the server, not the client). Here's a better solution:
if(Meteor.isClient){
// Insert the post and increase points.
Posts.insert({userId: Meteor.userId(), post: "The post."})
}
if(Meteor.isServer){
Posts.allow({
insert: function(userId, doc){
check(doc, {
_id: String,
userId: String,
post: String
})
// You must be yourself.
if(doc.userId != userId){
return false
}
return true
}
})
Posts.find().observe({
added: function(post){
// When new posts are added, the user gain the points.
Meteor.users.update(post.userId, {$inc: {'profile.points': 5}})
}
})
}
The only disadvantage this solution suffers from is the latency of the increment of the points, but it is something we must live with (at least at the moment). Using observe on the server may also be a disadvantage, but I think you can get pass it by using the package collection hooks instead.

Purely functional feedback suppression?

I have a problem that I can solve reasonably easy with classic imperative programming using state: I'm writing a co-browsing app that shares URL's between several nodes. The program has a module for communication that I call link and for browser handling that I call browser. Now when a URL arrives in link i use the browser module to tell the
actual web browser to start loading the URL.
The actual browser will trigger the navigation detection that the incoming URL has started to load, and hence will immediately be presented as a candidate for sending to the other side. That must be avoided, since it would create an infinite loop of link-following to the same URL, along the line of the following (very conceptualized) pseudo-code (it's Javascript, but please consider that a somewhat irrelevant implementation detail):
actualWebBrowser.urlListen.gotURL(function(url) {
// Browser delivered an URL
browser.process(url);
});
link.receivedAnURL(function(url) {
actualWebBrowser.loadURL(url); // will eventually trigger above listener
});
What I did first wast to store every incoming URL in browser and simply eat the URL immediately when it arrives, then remove it from a 'received' list in browser, along the lines of this:
browser.recents = {} // <--- mutable state
browser.recentsExpiry = 40000;
browser.doSend = function(url) {
now = (new Date).getTime();
link.sendURL(url); // <-- URL goes out on the network
// Side-effect, mutating module state, clumsy clean up mechanism :(
browser.recents[url] = now;
setTimeout(function() { delete browser.recents[url] }, browser.recentsExpiry);
return true;
}
browser.process = function(url) {
if(/* sanity checks on `url`*/) {
now = (new Date).getTime();
var duplicate = browser.recents[url];
if(! duplicate) return browser.doSend(url);
if((now - duplicate_t) > browser.recentsExpiry) {
return browser.doSend(url);
}
return false;
}
}
It works but I'm a bit disappointed by my solution because of my habitual use of mutable state in browser. Is there a "Better Way (tm)" using immutable data structures/functional programming or the like for a situation like this?
A more functional approach to handling long-lived state is to use it as a parameter to a recursive function, and have one execution of the function responsible for handling a single "action" of some kind, then calling itself again with the new state.
F#'s MailboxProcessor is one example of this kind of approach. However it does depend on having the processing happen on an independent thread which isn't the same as the event-driven style of your code.
As you identify, the setTimeout in your code complicates the state management. One way you could simplify this out is to instead have browser.process filter out any timed-out URLs before it does anything else. That would also eliminate the need for the extra timeout check on the specific URL it is processing.
Even if you can't eliminate mutable state from your code entirely, you should think carefully about the scope and lifetime of that state.
For example might you want multiple independent browsers? If so you should think about how the recents set can be encapsulated to just belong to a single browser, so that you don't get collisions. Even if you don't need multiple ones for your actual application, this might help testability.
There are various ways you might keep the state private to a specific browser, depending in part on what features the language has available. For example in a language with objects a natural way would be to make it a private member of a browser object.

Resources