An approach to deal with dependency resolution and optimistic updates in react applications - reactjs

In an architecture where objects have many complex relationships, what are some maintainable approaches to dealing with
Resolving Dependencies
Optimistic Updates
in react applications?
For example, given this type of schema:
```
type Foo {
...
otherFooID: String,
bars: List<Bar>
}
type Bar {
...
bizID: String,
}
type Biz {
...
}
```
A user might want to save the following ->
firstBiz = Biz();
secondBiz = Biz();
firstFoo = Foo({bars: [Bar({biz: firstBiz})]
secondFoo = Foo({bars: [Bar({biz: secondBiz})] otherFooId: firstFooId.id})
First Problem: Choosing real ids
The first problem with above is having the correct id. i.e in order for secondFoo to save, it needs to know the actual id of firstFoo.
To solve this, we could make the tradeoff, of letting the client choose the id, using something like a uuid. I don't see anything terribly wrong this this, so we can say this can work
Second Problem: Saving in order
Even if we determine id's from the frontend, the server still needs to receive these save requests in order.
```
- save firstFoo
// okay. now firstFoo.id is valid
- save secondFoo
// okay, it was able to resolve otherFooID to firstFoo
```
The reasoning here is that the backend must guarantee that any id that is being referenced is valid.
```
- save secondFoo
// backend throws an error otherFooId is invalid
- save firstfoo
// okay
```
I am unsure what the best way to attack this problem is
The current approaches that come to mind
Have custom actions, that do the coordination via promises
save(biz).then(_ => save(Bar).then(_ => save(firstFoo)).then(_ => save(second)
The downside here is that it is quite complex, and the number of these kinds of combinations will continue to grow
Create a pending / resolve helper
const pending = {}
const resolve = (obj, refFn) => {
return Promise.all(obj, refFn(obj));
}
const fooRefs = (foo) => {
return foo.bars.map(bar => bar.id).concat(foo.otherFooId);
}
pending[firstFoo].id = resolve(firstFoo, fooRefs).then(_ => save(firstFoo))
```
The problem with 2. is that it can cause a bunch of errors easily, if we forget to resolve or to add to pending.
Potential Solutions
It seems like Relay or Om next can solve these issues, but i would like something less high power. Perhaps something that can work in with redux, or maybe it's some concept I am missing.
Thoughts much appreciated

I have a JS/PHP implementation of such a system
My approach is to serialize records both on the client and server using a reference system
For example unsaved Foo1 has GUID eeffa3, and a second Foo references its id key as {otherFooId: '#Foo#eeffa3[id]' }
Similarily you can reference a whole object like this
Foo#eefa3:{bars['#Baz#ffg4', '#Baz#ffg5']}
Now the client-side serializer would build a tree of relations and model attributes like this
{
modelsToSave:[
'Foo#effe3':{
attribs:{name:'John', title:'Mr.'},
relations:{bars:['#Bar#ffg4']}
},
'Bar#ffg4':{
attribs:{id:5}
relations:{parentFoo:'#Foo#effe3'}
},
]
}
As you can see in this example I have described circular relations between unsaved objects in pure JSON.
The key here is to hold these "record" objects in client-side memory and never mutate their GUID
The server can figure out the order of saving by saving first records without "parent" dependencies, then records which depend on those parents
After saving, the server wil return the same reference map, but now the attribs will also include primary keys and foreign keys
JS walks the received map twice (first pass just update server-received attributes, second pass substitute record references and attribute references to real records and attributes).
So there are 2 mechanisms for referencing a record, a client-side GUID and a server-side PK
When receiving a server JSON, you match your GUID with the server primary key

Related

Apollo Client readFragment with custom id (keyFields)

For ref, using "#apollo/client": "^3.5.5",
I've defined my typePolicies like so as suggested in docs:
HistoricalData: {
keyFields: ["variable", "workspace"],
fields:{...}
}
and when my cache is built, I am expecting my cacheId to be like
<__typename>:<id>:<id>
HistoricalData:${props.variable}:${props.workspace}`;
but instead, when I look in the Apollo cache, it's been created using the keyField names and the values in an object, such as
HistoricalData:{"variable":"GAS.TOTAL","workspace":"ABC"}
instead of
HistoricalData:GAS.TOTAL:ABC
so when I try to readFragment it returns null
client.readFragment({
id: `HistoricalData:${props.variable}:${props.workspace}`,
fragment: apolloGQL`fragment MyHistorical on Historical {
variable
workspace
}`})
It does actually return a value from the cache if I create my id in the structure that exists in the cache and readFragment using this.
Has anyone else noticed that Apollo client is not creating the cache id's in the structure that they describe in the docs?
After some research I came upon the correct way to handle this case. I know that you have already moved on, but just in case anyone else has the same problem in the future, here goes:
As described in the documentation for customizing the cache ID, the cache ID will be an stringified object, as you pointed out. It's not quite explicit in the documentation, but at this point in time it provides this nested example for a cache ID:
Book:{"title":"Fahrenheit 451","author":{"name":"Ray Bradbury"}}
But as users we don't have to preoccupy us with the format of this ID, because there's a helper for that, called cache.identify.
For your specific case, you could use something like this:
const identifiedId = cache.identify({
__typename: 'HistoricalData',
variable: 'GAS.TOTAL',
workspace: 'ABC'
});
cache.readFragment({
id: identifiedId,
fragment: apolloGQL`fragment MyHistorical on Historical {
variable
workspace
}`
});

CakePHP3: Check if model exists

I have a search engine which calls a Cakephp action and receives which model the engine should search in eg. "Projects". The variable is called $data_type;
Right now I use this to check if the model exists:
// Check if Table really exists
if(!TableRegistry::get($data_type)){
// Send error response to view
$response = [
'success' => false,
'error' => 'Data type does not exist'
];
$this->set('response', $response);
return;
}
I'm not sure I'm doing it the right or the safest way to check if a model exists, because I don't know if the TableRegistry::get() function is vulnerable to SQL injection behind the scenes.
I also found that inputing an empty string to the get() function doesn't need in a false result??? Is there a safe solution I can implement that will solve my problem?
TableRegistry::get() is not safe to use with user input
First things first. It's probably rather complicated to inject dangerous SQL via TableRegistry::get(), but not impossible, as the alias passed in the first argument will be used as the database table name in case an auto/generic-table instance is created. However the schema lookup will most likely fail before anything else, also the name will be subject to inflection, specifically underscore and lowercase inflection, so an injection attempt like
Foo; DELETE * FROM Bar;
would end up as:
foo;d_e_l_e_t_e*f_r_o_m_bar;
This would break things as it's invalid SQL, but it won't cause further harm. The bottom line however is that TableRegistry::get() cannot be regarded as safe to use with user input!
The class of the returned instance indicates a table class' existence
TableRegistry::get() looks up and instantiates possible existing table classes for the given alias, and if that fails, it will create a so called auto/generic-table, which is an instance of \Cake\ORM\Table instead of an instance of a concrete subclass thereof.
So you could check the return value against \Cake\ORM\Table to figure whether you've retrieved an instance of an actual existing table class:
$table = TableRegistry::get($data_type);
if (get_class($table) === \Cake\ORM\Table::class) {
// not an existing table class
// ...
}
Use a whitelist
That being said, unless you're working on some kind of administration tool that explicitly needs to be able to access to all tables, the proper thing do would be to use some sort of whitelisting, as having users arbitrarily look up any tables they want could be a security risk:
$whitelist = [
'Projects',
'...'
];
if (in_array($data_type, $whitelist, true) !== true) {
// not in the whitelist, access prohibited
// ...
}
Ideally you'd go even further and apply similar restrictions to the columns that can be looked up.
You may want to checkout https://github.com/FriendsOfCake/awesome-cakephp#search for some ready made search plugins.

Doctrine update model with oneToMany fields

Couldn't get much help from the World Wide Wide Wide Web ...
The situation is:
My model has two manyToMany fields and one oneToMany field.
When I update the model, doctrine magically remove the old values for manyToMany fields and set the new one,
but for the oneToMany field it does not remove the old value but add the new one.
Is this a normal behaviour?
To work around I am DQLing to empty the oneToMany field target Entity for the mapped by model id and persisting new values
i.e Model: Rule has
/**
* #OneToMany(targetEntity="\Tier", mappedBy="rule", cascade={"persist", "remove"})
* #JoinColumn(name="ruleId", referencedColumnName="ruleId")
* #var Tier[]
*/
public $tiers;
And in the Rule Mapper I am deleting all tiers before calling PERSIST to get UPDATE working:
public function resetTiers($ruleId)
{
$modelClass = "Tier";
$q = $this->doctrineEntityManager->createQuery("Delete from $modelClass m where m.rule =" . $ruleId);
$numDeleted = $q->execute();
return $numDeleted;
}
I don't mind doing this way as long as its OK and not introducing any bad practice.
Thank you for your time.
Please read this bit of Doctrine's documentation.
Doctrine will only check the owning side of an association for changes.
And:
OneToMany is always the inverse side of a bidirectional association.
Although:
You can pick the owning side of a many-to-many association yourself.
So yes, it's normal behavior.
My guess is your ManyToMany relations are owned by your model (i.e. the Doctrine declaration you put in your property's comment says inversedBy and not mappedBy). Which would be why they're automatically updated (as per the doc linked above).
So either you start working on the other side's entities (namely Tier), or you trick the ORM by updating the other side's entities directly through your Rule's accessors, probably something like:
public function setTiers($tiers) {
foreach ($this->tiers as $tier) {
$tier->setRule(null);
}
foreach ($tiers as $tier) {
$tier->setRule($this);
}
if (is_array($tiers)) {
//this wrapper is useful if you use syntaxes like
//$someRule->getTiers()->add($someTier); which is usual if you use
//an addTier(Tier $tier) method declared in Rule
$tiers = new \Doctrine\Common\Collections\ArrayCollection($tiers);
}
$this->tiers = $tiers;
}
For the first option, working on other side's entities, just calling $someTier->setRule($someRule) will auto-update* $someRule's $tiers.
*: auto-update should happen 1/ when you flush your mods and 2/ maybe you'll need to refresh the $someRule object (I mean the EM's refresh method).
All of this is "my guess", anyone feel free to correct, and OP feel free to give some feedback about how it went if you try this! ;)
Here is some more doc about updating related entities, especially at the bottom of the page, section 6.14.1.
Hope this helps!

AngularFire - How do I query denormalised data?

Ok Im starting out fresh with Firebase. I've read this: https://www.firebase.com/docs/data-structure.html and I've read this: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html
So I'm suitably confused as one seems to contradict the other. You can structure your data hierarchically, but if you want it to be scalable then don't. However that's not the actual problem.
I have the following structure (please correct me if this is wrong) for a blog engine:
"authors" : {
"-JHvwkE8jHuhevZYrj3O" : {
"userUid" : "simplelogin:7",
"email" : "myemail#domain.com"
}
},
"posts" : {
"-JHvwkJ3ZOZAnTenIQFy" : {
"state" : "draft",
"body" : "This is my first post",
"title" : "My first blog",
"authorId" : "-JHvwkE8jHuhevZYrj3O"
}
}
A list of authors and a list of posts. First of all I want to get the Author where the userUid equals my current user's uid. Then I want to get the posts where the authorId is the one provided to the query.
But I have no idea how to do this. Any help would be appreciated! I'm using AngularFire if that makes a difference.
Firebase is a NoSQL data store. It's a JSON hierarchy and does not have SQL queries in the traditional sense (these aren't really compatible with lightning-fast real-time ops; they tend to be slow and expensive). There are plans for some map reduce style functionality (merged views and tools to assist with this) but your primary weapon at present is proper data structure.
First of all, let's tackle the tree hierarchy vs denormalized data. Here's a few things you should denormalize:
lists you want to be able to iterate quickly (a list of user names without having to download every message that user ever wrote or all the other meta info about a user)
large data sets that you view portions of, such as a list of rooms/groups a user belongs to (you should be able to fetch the list of rooms for a given user without downloading all groups/rooms in the system, so put the index one place, the master room data somewhere else)
anything with more than 1,000 records (keep it lean for speed)
children under a path that contain 1..n (i.e. possibly infinite) records (example chat messages from the chat room meta data, that way you can fetch info about the chat room without grabbing all messages)
Here's a few things it may not make sense to denormalize:
data you always fetch en toto and never iterate (if you always use .child(...).on('value', ...) to fetch some record and you display everything in that record, never referring to the parent list, there's no reason to optimize for iterability)
lists shorter than a hundred or so records that you always as a whole (e.g. the list of groups a user belongs to might always be fetched with that user and would average 5-10 items; probably no reason to keep it split apart)
Fetching the author is as simple as just adding the id to the URL:
var userId = 123;
new Firebase('https://INSTANCE.firebaseio.com/users/'+userId);
To fetch a list of posts belonging to a certain user, either maintain an index of that users' posts:
/posts/$post_id/...
/my_posts/$user_id/$post_id/true
var fb = new Firebase('https://INSTANCE.firebaseio.com');
fb.child('/my_posts/'+userId).on('child_added', function(indexSnap) {
fb.child('posts/'+indexSnap.name()).once('value', function(dataSnap) {
console.log('fetched post', indexSnap.name(), dataSnap.val());
});
});
A tool like Firebase.util can assist with normalizing data that has been split for storage until Firebase's views and advanced querying utils are released:
/posts/$post_id/...
/my_posts/$user_id/$post_id/true
var fb = new Firebase('https://INSTANCE.firebaseio.com');
var ref = Firebase.util.intersection( fb.child('my_posts/'+userId), fb.child('posts') );
ref.on('child_added', function(snap) {
console.log('fetched post', snap.name(), snap.val();
});
Or simply store the posts by user id (depending on your use case for how that data is fetched later):
/posts/$user_id/$post_id/...
new Firebase('https://INSTANCE.firebaseio.com/posts/'+userId).on('child_added', function(snap) {
console.log('fetched post', snap.name(), snap.val());
});

breeze - how to subscribe to changes on a many-to-many relationship

I've got a many-to-many relationship in Breeze:
Product *---1 ProductWidgets 1----* Widgets
Product needs to know when any of it's Widgets changes. Widgets can be added or removed from Products at any time.
Ideally, I'd want to do something like:
product.widgets.on('change', function () {});
...but I'm imagining I need something like:
var handleWidgetChange = function (changes) {
console.log("here are the changes", changes);
};
for(var i = 0; i < product.productWidgets.length; i++) {
// make sure we're getting events for the current set of widgets
product.productWidgets[i].widget.entityAspect.propertyChanged.subscribe(handleWidgetChange);
// if any of the current set of product widgets gets pointed elsewhere, catch that
product.productWidgets[i].entityAspect.propertyChanged.subscribe(function (change) {
if (change.propertyName === "widget") {
change.oldValue.entityAspect.propertyChanged.unsubscribe();
change.oldValue.entityAspect.propertyChanged.subscribe(handleWidgetChange);
}
})
}
// handle new product widgets and removal of product widgets
product.productWidgets.arrayChanged.subscribe(function (change) {
if (change.added) {
change.added[0].widget.entityAspect.propertyChanged.subscribe(handleWidgetChange);
} else if (change.removed) {
change.removed[0].widget.entityAspect.propertyChanged.unsubscribe();
}
});
Is there a recommended way to achieve this?
(Note: I'm using angular, and would love to just $watch('product.productWidgets', function () {}, true) but that gives a circular reference error.)
Memory leaks are a huge risk in JavaScript, in part because there are no weak references. You must be careful with events. You really don't want to iterate over entities adding and removing subscriptions.
You also do not want to use Angular watches for monitoring model changes because you'll drive UI performance into the ground. There are too many entities with too many properties and you'll surely make a mistake by leaving watches in place long after you should have stopped watching.
Fortunately, Breeze provides a central entity change monitoring facility. A Breeze EntityManager listens for changes to any of the entities it holds in cache.
var widgetType = manager.metadataStore.getEntityType('Widget');
var productWidgetType = manager.metadataStore.getEntityType('ProductWidget');
entityManager.entityChanged.subscribe(entityChanged);
function entityChanged(changeArgs) {
var entity = changeArgs.entity;
if (entity.entityType === productWidgetType ||
entity.entityType === widgetType) {
// do what you do when someone does something to an entity of this type
// perhaps call back into a method on that instance that knows what to do
entity.somethingChanged(changeArgs.entityAction);
}
}
This one event notifies you of any change to any entity in the manager's cache. It will be called frequently so be crisp in your evaluation. For example, consider deactivating your event handler during queries.
The changeArgs.entityAction tells you what just happened to the entity. There are many actions that trigger this event: a property could change, its EntityState could change (add/modify/delete/detach), etc.
You don't have to worry about the product.productWidgets array. When a ProductWidget is added or removed from that array, the ProductWidget.productId foreign key will change ... and you're picking that up in this entityChanged handler.
There is no need to worry about a memory leak because the EntityManager already holds a reference to the entity and will continue to do so until you detach the entity or dispose of the EntityManager instance (and all of your own or the UI's references to the entity). That, to my mind, is appropriate lifetime management.

Resources