How to handle circularly dependent observables in RxJS? - circular-dependency

Let's say e.g. that somewhere on a server there is a mapping between
integers and names and a web page provides a simple input where a user
can enter a number and is given the corresponding name.
In its basic form, this problem is simple:
const input$ = Rx.Observable.fromEvent(..., "input");
const request$ = input$.map( ... );
const serverResponse$ = request$.flatMap( askServer );
Now I would like to cache the results so that a request is only
done when the number is not in the cache.
const input$ = Rx.Observable.fromEvent(..., "input");
// request$ should now also depend on cache$
const request$ = ???;
const serverResponse$ = request$.flatMap( askServer );
const cache$ = serverResponse$.scan( ... );
But now request$ depends on cache$ which depends on a serverResponse$
which in turn depends on request$.
How do I solve this problem?

Introduce a Subject as a proxy at some point in the cycle in the dependency graph, then mirror the behavior of the real Observable (cache$) onto the proxy Subject (proxyCache$).
const input$ = Rx.Observable.fromEvent(..., "input");
const proxyCache$ = new Rx.Subject();
const request$ = input$.merge(proxyCache$).map( ... );
const serverResponse$ = request$.flatMap( askServer );
const cache$ = serverResponse$.scan( ... );
cache$.subscribe(proxyCache$);

Related

Chrome resource loading behaviour

Today I ran into rather a strange behaviour of Chrome. I was playing with PerformanceObserver and found out that when you add two stylesheets with the same URL to the DOM very quickly then chrome fires only one request which obviously makes sense as it saves network load.
const testCase = async () => {
let numberOfRecords = 0
const observer = new PerformanceObserver((entryList) => {
const performanceEntries = entryList.getEntries()
numberOfRecords += performanceEntries.length
})
observer.observe({ entryTypes: ['resource'] })
// Test: Only one performance record is created because links are added at the same time
// and chrome detects duplicate request
const linkElement1 = document.createElement('link')
linkElement1.rel = 'stylesheet'
linkElement1.href = 'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'
document.head.appendChild(linkElement1)
const linkElement2 = document.createElement('link')
linkElement2.rel = 'stylesheet'
linkElement2.href = 'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'
document.head.appendChild(linkElement2)
// wait a little bit so performance observer callback is called
await new Promise((resolve) => {
setTimeout(() => resolve(), 1000)
})
console.assert(numberOfRecords === 1, 'Test')
console.log('Test finished')
}
testCase()
When sleep time is added between adding link nodes to DOM then chrome fires two requests (the second one is taken from cache)
const testCase = async () => {
let numberOfRecords = 0
const observer = new PerformanceObserver((entryList) => {
const performanceEntries = entryList.getEntries()
numberOfRecords += performanceEntries.length
})
observer.observe({ entryTypes: ['resource'] })
// Test: Only one performance record is created because links are added at the same time
// and chrome detects duplicate request
const linkElement1 = document.createElement('link')
linkElement1.rel = 'stylesheet'
linkElement1.href = 'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'
document.head.appendChild(linkElement1)
// wait here so chrome triggers two requests
await new Promise((resolve) => {
setTimeout(() => resolve(), 1000)
})
const linkElement2 = document.createElement('link')
linkElement2.rel = 'stylesheet'
linkElement2.href = 'https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css'
document.head.appendChild(linkElement2)
// wait a little bit so performance observer callback is called
await new Promise((resolve) => {
setTimeout(() => resolve(), 1000)
})
console.assert(numberOfRecords === 2, 'Test')
console.log('Test finished')
}
testCase()
However when I run this second code via automated test (webdriver.io) or I try it on cloud service like Browserstack/Lambdatest (the same browser version, OS version) it fails as it triggers only one request. So I wonder what's the difference?
To see it by yourself you can open some empty page (it's quite important that page is empty and doesn't contain any background requests) and copy the code examples to console.
I just wonder whether you disabled the cache for the automation tests. In Chrome, apparently if you didn't tick "Disable cache", then you second test should fail.

Forge Viewer: Properties Window

The Properties window does not populate any properties even though the 2D view has properties info for the selected room
Here is the function that loads the model. what am I missing?
function loadModel() {
var initialViewable = viewables[indexViewable];
var svfUrl = lmvDoc.getViewablePath(initialViewable);
var modelOptions = {
sharedPropertyDbPath: lmvDoc.getFullPath(lmvDoc.getRoot().findPropertyDbPath())
};
viewer.loadModel(svfUrl, modelOptions, onLoadModelSuccess, onLoadModelError);
}
One line missing in your code, please try the following instead:
var sharedDbPath = initialViewable.findPropertyDbPath();
sharedDbPath = lmvDoc.getFullPath( sharedDbPath );
var modelOptions = {
sharedPropertyDbPath: sharedDbPath
};
However, you should not need to specify the sharedPropertyDbPath manually now. You can take advantage of the Viewer3D#loadDocumentNode to load the model directly. It will automatically determine the path for you. (started from v7 viewer)
const initialViewable = viewables[0];
viewer.loadDocumentNode( lmvDoc, initialViewable, loadOptions )
.then( onLoadModelSuccess )
.catch( onLoadModelError );

How i can execute multiple queries (UPDATE) in NODE using Sequelize?

I need to execute multiples queries in NODE using Sequelize.
I tried execute with a for, but it's didn't work.
Someone can help me?
exports.update = async (req, res) => {
for (let i = 0; i < req.body.length; i++) {
const id = req.body[i].id;
const permissao = req.body[i].permissao;
const sql =
`UPDATE tetsiste_usuarios.usuarioGraficos SET permissao = ${permissao} \n
WHERE id = ${id} AND permissao <> ${permissao};`;
sequelize.query(sql, { type: Sequelize.QueryTypes.UPDATE })
.then(data => res.json(data))
}
}
You need to await the call the sequelize.query but that said... you are doing this all wrong. The idea behind using an ORM like Sequelize is that it abstracts the SQL and provides protection against things like SQL injection attacks and malormed queries, which your code is susceptible to. If I mess with the request and pass in this for permissao it will drop your database table 1; DROP TABLE usuarioGraficos; SELECT 1 FROM dual. That is bad. You're also calling res.json() on every loop, which will also result in an error.
The proper way to do this is to use the Model.create() function for inserts or Model.update() for updates.
loop insert with sql injection vulnerability
const Sequelize = require('sequelize');
const models = require('./models'); // your model definitions
// an array to store our results
const updates = [];
// use a forEach to get each entry from the body array
for (let i = 0; i < req.body.length; i++) {
// this ID
const id = req.body[i].id;
// this permission
const permissao = req.body[i].permissao;
// update the permission where the ID matches and the permission does not
const update = await models.UsuarioGraficos.update({ permissao }, {
where: {
id,
permissao: {
[Sequelize.Op.ne]: permissao,
},
},
});
// add the result
updates.push(update);
}
// send all the updates outside the loop
res.json(updates);

How to apply expiry times to keys in HTML5 indexeddb?

In indexedDB in html5 api, I can use it to store key-value pairs. But how can I make sure that after adding a certain key-value, after 1 day, that key should automatically be deleted from the db.
I was thinking of wrapping the value in an object with current datetime, and expiry time, and when u get the value, check the time difference, but is this the best way?
Thanks
Yep, what Scott Marcus and dgrogan said. One more hint: if you create an index on the timestamp, you can iterate a cursor over the range of "expired" values and delete them after opening the database.
const open = indexedDB.open("demo");
open.onupgradeneeded = function () {
const db = open.result;
const store = db.createObjectStore("store");
const index = store.createIndex("timestamp", "timestamp");
// Populate with some dummy data, with about half from the past:
for (let id = 0; id < 20; ++id) {
store.put(
{
value: Math.random(),
timestamp: new Date(Date.now() + (Math.random() - 0.5) * 10000),
},
id
);
}
};
open.onsuccess = function () {
const db = open.result;
const tx = db.transaction("store", "readwrite");
// Anything in the past:
const range = IDBKeyRange.upperBound(new Date());
tx
.objectStore("store")
.index("timestamp")
.openCursor(range).onsuccess = function (e) {
const cursor = e.target.result;
if (!cursor) return;
console.log("deleting: " + cursor.key);
cursor.delete();
cursor.continue();
};
// This transaction will run after the first commits since
// it has overlapping scope:
const tx2 = db.transaction("store");
tx2.objectStore("store").count().onsuccess = function (e) {
console.log("records remaining: " + e.target.result);
};
};
Support for automatically expiring IndexedDB and other storage data is being considered. See https://github.com/whatwg/storage/issues/11. It would be helpful if you could describe your use case there.
In the meantime you'll have to do something like you outlined or what Scott Marcus suggests.
IndexedDB can only be modified by code. It has no automatic capabilities. Why not just store the data with a timestamp value and modify your code to remove it when the timestamp is out of date?
I wrote a npm module sometime back which expires indexedDB keys after x mins. Also it has very simple api (similar to memcache/localstorage) and is suited for use as a key-value store.
You can check it on npm here .

What is the new way of obtaining current query from router context in v2.x?

I am upgrading to v2 and can't seem to find in the docs an example of how to access the current query from the router context (or other means).
Pertinent parts of the components:
var React = require('react');
var ReactDOM = require('react-dom');
var ReactRouter = require('react-router');
var Router = ReactRouter.Router;
var routes = require('./Routes');
var browserHistory = ReactRouter.browserHistory;
var App = {
blastoff: function () {
this.mainElement = ReactDOM.render((<Router history = {browserHistory}>{routes}</Router>),
window.document.getElementById('app-mount')
);
}
};
........
contextTypes: {
router: React.PropTypes.func
},
.......
this.context.router.getCurrentQuery()
How should this be done in v2.x? Is there a completely different way of getting the current query?
Thanks!
It's not directly available via non-deprecated APIs. The location prop is made available to route components, and you can pass the query information down from there as appropriate.