Sort connection in relay modern store client side - relaymodern

I have a mutation that reorders two edges in a relay connection. What is the right way to perform this reordering optimistically? I've tried an in-place sort like:
const conn = ConnectionHandler.getConnection(pathway, 'PathwayGraph_actions');
const edges = conn.getLinkedRecords('edges');
edges.sort((a, b) => {
const laneA = a.getLinkedRecord('node').getValue('laneNumber');
const laneB = b.getLinkedRecord('node').getValue('laneNumber');
return laneA - laneB;
});
This doesn't seem to work. I suspect relay tries pretty hard to not allow store mutations except through its interface. I suppose I could use ConnectionHandler to delete all the existing edges, and then add them all back in the proper order, but that sounds quite cumbersome. Is there a better way?

RecordProxy.setLinkedRecords made this not so awful:
edges.sort((a, b) => {
const laneA = a.getLinkedRecord('node').getValue('laneNumber');
const laneB = b.getLinkedRecord('node').getValue('laneNumber');
return laneA - laneB;
});
conn.setLinkedRecords(edges, 'edges');
Still a bit curious if anyone has a sleeker way.

Related

Cypress: Get JQuery value without needing `then` or `each`

I'm hoping someone can help, but I've posted this as a Cypress discussion as well, although it might just be my understanding that's wrong
I need to get the Cypress.Chainable<JQuery<HTMLElement>> of the cell of a table using the column header and another cell's value in the same row.
Here's a working example JQuery TS Fiddle: https://jsfiddle.net/6w1r7ha9/
My current implementation looks as follows:
static findCellByRowTextColumnHeaderText(
rowText: string,
columnName: string,
) {
const row = cy.get(`tr:contains(${rowText})`);
const column = cy.get(`th:contains(${columnName})`)
const columnIndex = ???
return row.find(`td:eq(${columnIndex})`)
}
This function is required because I want to write DRY code to find cells easily for content verification, clicking elements inside of it etc.
The only example I've seen is this https://stackoverflow.com/a/70686525/1321908, but the following doesn't work:
const columns = cy.get('th')
let columnIndex = -1
columns.each((el, index) => {
if (el.text().includes(columnName) {
columnIndex = index
}
cy.log('columnIndex', columnIndex) // Outputs 2 as expected
})
cy.log('finalColumnIndex', columnIndex) // Outputs -1
My current thinking is something like:
const columnIndex: number = column.then((el) => el.index())
This however returns a Chainable<number> How to turn it into number, I have no idea. I'm using this answer to guide my thinking in this regard.
Using .then() in a Cypress test is almost mandatory to avoid flaky tests.
To avoid problems with test code getting ahead of web page updating, Cypress uses Chainable to retry the DOM query until success, or time out.
But the Chainable interface isn't a promise, so you can't await it. You can only then() it.
It would be nice if you could substitute another keyword like unchain
const column = unchain cy.get(`th:contains(${columnName})`)
but unfortunately Javascript can't be extended with new keywords. You can only add methods like .then() onto objects like Chainable.
Having said that, there are code patterns that allow extracting a Chainable value and using it like a plain Javascript variable.
But they are limited to specific scenarios, for example assigning to a global in a before() and using it in an it().
If you give up the core feature of Cypress, the automatic retry feature, then it's just jQuery exactly as you have in the fiddle (but using Cypress.$() instead of $()).
But even Mikhail's thenify relys on the structure of the test when you add a small amount of asynchronicity
Example app
<foo>abc</foo>
<script>
setTimeout(() => {
const foo = document.querySelector('foo')
foo.innerText = 'def'
}, 1000)
</script>
Test
let a = cy.get("foo").thenify()
// expect(a.text()).to.eq('def') // fails
// cy.wrap(a.text()).should('eq', 'def') // fails
cy.wrap(a).should('have.text', 'def') // passes
let b = cy.get("foo") // no thenify
b.should('have.text', 'def') // passes
Based on your working example, you will need to get the headers first, map out the text, then find the index of the column (I've chosen 'Col B'). Afterwards you will find the row containing the other cell value, then get all the cells in row and use .eq() with the column index found earlier.
// get headers, map text, filter to Col B index
cy.get("th")
.then(($headers) => Cypress._.map($headers, "innerText"))
.then(cy.log)
.invoke("indexOf", "Col B")
.then((headerIndex) => {
// find row containing Val A
cy.contains("tbody tr", "Val A")
.find("td")
// get cell containing Val B
.eq(headerIndex)
.should("have.text", "Val B");
});
Here is the example.

Resolve array of observables and append in final array

I have an endpoint url like http://site/api/myquery?start=&limit= which returns an array of strings.
If I call this endpoint in this way, the server hangs since the array of strings length is huge.
I need to generate an an array of observables with incremental "start" and "limit" parameters, resolve all of then either in sequence or in parallel, and then get a final observable which at the end yields the true array of strings, obtained merging all the subarray of strings returned by the inner observables.
How should I do that?
i.e. the array of observables would be something like
[
httpClient.get(http://site/api/myquery?start=0&limit=1000),
httpClient.get(http://site/api/myquery?start=1000&limit=1000),
httpClient.get(http://site/api/myquery?start=2000&limit=1000),
....
]
If you know the length before making all these queries — then you can create as many http-get Observables as you need, and then forkJoin them using projection fn.
forkJoin will let you make parallel queries and then merge results of those queries. Heres an example:
import { forkJoin } from 'rxjs';
// given we know the length:
const LENGTH = 500;
// we can pick arbitrary page size
const PAGE_SIZE = 50;
// calculate requests count
const requestsCount = Math.ceil(LENGTH / 50);
// generate calculated number of requests
const requests = (new Array(requestsCount))
.fill(void 0)
.map((_,i) => {
const start = i * PAGE_SIZE;
return http.get(`http://site/api/myquery?start=${start}&limit=${PAGE_SIZE}`);
});
forkJoin(
requests,
// projecting fn
// merge all arrays into one
// suboptimal merging, just for example
(...results) => results.reduce(((acc, curr)=> [...acc, ...curr]) , [])
).subscribe(array => {
console.log(array);
})
Check this forkJoin example for reference.
Hope this helps
In the case that you do not know the total number of items, you can do this using expand.
The following article gives a good introduction to expand and an explanation of how to use it for pagination.
https://ncjamieson.com/understanding-expand/
Something along the lines of the code below would work in your case, making the requests for each page in series.
const limit = 1000;
let currentStart = 0;
let getUrl = (start, limit) => `http://site/api/myquery?start=${start}&limit=${limit}`;
httpClient.get(getUrl(currentStart, limit)).pipe(
expand(itemsArray => {
if (itemsArray.length) {
currentStart += limit;
return httpClient.get(getUrl(currentStart, limit));
}
return empty();
}),
reduce((acc, value) => [...acc, ...value]),
).subscribe(itemsArray => {
console.log(itemsArray);
})
This will log out the final array of items once the entire series of requests has been resolved.

Filter an observable by an observable

Let's consider the following simplified situation:
We have an Observable apples of type Observable < Apple >
Every Apple object has a method isRotten() which returns an observable of type Observable < Boolean > which is guaranteed to emit at least one boolean value.
I want to filter the apples observable such that the rotten apples don't pass the filter. More precisely, an apple A passes the filter if and only if the first item emitted by A.isRotten() is false. What is the best way to implement this filter?
After some thinking I could come up with this:
apples
.concatMap(apple =>
apple.isRotten()
.first()
.filter(bool => bool)
.map(bool => apple))
Which is written in javascript. ( ... => ... is a function). This works, but I think it is rather lengthy and difficult to understand. Is there a better way to do this kind of thing?
What you've got is fine and, tbh, I can't think of a more concise way of doing it. I'd probably use flatMap rather than concatMap if out-of-order apples aren't an issue.
If readibility is an issue for you, just move the implementation into it's one function (eg. filterObservable that accepts a function that takes a value and returns an IObservable<bool>)
One way to achieve that is like this, sorry I didn't get to adapt this to fruit filtering:
const orders$: Observable<Order[]> = searchOrders(...);
const filteredOrders$ = orders$.pipe(switchMap((orders) => {
// take all orders and determine visibility based on an observable
const visibilityRules = orders.map(o => {
return {
order: o,
visible$: o.isPaidFor$ // this is an observable on an Order object
};
});
const filtered = visibilityRules.map(o => o.visible$.pipe(map(visible => visible ? o.order : undefined )));
return (filtered.length == 0) ? of([]) : combineLatest(filtered).pipe(map(combined => combined.filter(x => x != undefined)));
}));
This filters 'paidFor' orders and emits a new array every time an order becomes paid or unpaid.
Note: If the isPaidFor$ observable can't change between searches then this whole exercise is pointless because there would be no reason to provide a 'live view' like this. This only makes sense if the observable can actually change between search results.
This could be extended to much more complicated rules if needed (such as adding filtering checkboxes) - that's why I created the intermediate visibilityRules array - which strictly speaking is just for readability.
You can do something like this:
var seq = Rx.Observable.from([1, 2, 3, 4, 5, 6])
.filter(x => {
let isRotten = true;
Rx.Observable.just(x % 2 === 0)
.first()
.subscribe(val => isRotten = val);
if (isRotten) {
return x;
}
});
seq.subscribe(x => console.log(x));

Can you convert this Linq statement into Lambda without using join statements?

I see this all over the web, however I'm curious if there isn't an easier way to write this in lambda?
var x = from c in db.Client
from p in db.Prospects
from ct in db.Countys
select new ViewModelExcelReport
{
client = c.ClientName,
cntyCounty = ct.County,
sctSection = p.Section
};
I would like to see a lambda expression that does NOT use joins, as though i am almost certain that i have seen one without the joins, but if this isn't possible ofcouse i'd like to see one with, thanks.
Basically multiple from clauses contribute SelectMany calls. So your code is something like:
var x = db.Client
.SelectMany(c => db.Prospects, (c, p) => new { c, p })
.SelectMany(z => db.Countys, (z, ct) => new { z, ct })
.Select(zz => new ViewModelExcelReport
{
client = zz.z.c.ClientName,
cntyCounty = zz.ct.County,
sctSection = zz.z.p.Section
});
Note how this is rather more longwinded than the query expression - the compiler takes care of keeping track of all the range variables via transparent identifiers. Why do you want this in lambda form? What do you see as being the benefit? The query expression will be translated into exactly the same code, so you should use whichever one is clearer - which in this case looks like the query expression, IMO.
As an aside, I'd strongly recommend that you change your property names (in ViewModelExcelReport) to more idiomatic ones if you possibly can.

When is LINQ (to objects) Overused?

My career started as a hard-core functional-paradigm developer (LISP), and now I'm a hard-core .net/C# developer. Of course I'm enamored with LINQ. However, I also believe in (1) using the right tool for the job and (2) preserving the KISS principle: of the 60+ engineers I work with, perhaps only 20% have hours of LINQ / functional paradigm experience, and 5% have 6 to 12 months of such experience. In short, I feel compelled to stay away from LINQ unless I'm hampered in achieving a goal without it (wherein replacing 3 lines of O-O code with one line of LINQ is not a "goal").
But now one of the engineers, having 12 months LINQ / functional-paradigm experience, is using LINQ to objects, or at least lambda expressions anyway, in every conceivable location in production code. My various appeals to the KISS principle have not yielded any results. Therefore...
What published studies can I next appeal to? What "coding standard" guideline have others concocted with some success? Are there published LINQ performance issues I could point out? In short, I'm trying to achieve my first goal - KISS - by indirect persuasion.
Of course this problem could be extended to countless other areas (such as overuse of extension methods). Perhaps there is an "uber" guide, highly regarded (e.g. published studies, etc), that takes a broader swing at this. Anything?
LATE EDIT: Wow! I got schooled! I agree I'm coming at this entirely wrong-headed. But as a clarification, please take a look below at sample code I'm actually seeing. Originally it compiled and worked, but its purpose is now irrelevant. Just go with the "feel" of it. Now that I'm revisiting this sample a half year later, I'm getting a very different picture of what is actually bothering me. But I'd like to have better eyes than mine make the comments.
//This looks like it was meant to become an extension method...
public class ExtensionOfThreadPool
{
public static bool QueueUserWorkItem(Action callback)
{
return ThreadPool.QueueUserWorkItem((o) => callback());
}
}
public class LoadBalancer
{
//other methods and state variables have been stripped...
void ThreadWorker()
{
// The following callbacks give us an easy way to control whether
// we add additional headers around outbound WCF calls.
Action<Action> WorkRunner = null;
// This callback adds headers to each WCF call it scopes
Action<Action> WorkRunnerAddHeaders = (Action action) =>
{
// Add the header to all outbound requests.
HttpRequestMessageProperty httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add("user-agent", "Endpoint Service");
// Open an operation scope - any WCF calls in this scope will add the
// headers above.
using (OperationContextScope scope = new OperationContextScope(_edsProxy.InnerChannel))
{
// Seed the agent id header
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestMessage;
// Activate
action();
}
};
// This callback does not add any headers to each WCF call
Action<Action> WorkRunnerNoHeaders = (Action action) =>
{
action();
};
// Assign the work runner we want based on the userWCFHeaders
// flag.
WorkRunner = _userWCFHeaders ? WorkRunnerAddHeaders : WorkRunnerNoHeaders;
// This outter try/catch exists simply to dispose of the client connection
try
{
Action Exercise = () =>
{
// This worker thread polls a work list
Action Driver = null;
Driver = () =>
{
LoadRunnerModel currentModel = null;
try
{
// random starting value, it matters little
int minSleepPeriod = 10;
int sleepPeriod = minSleepPeriod;
// Loop infinitely or until stop signals
while (!_workerStopSig)
{
// Sleep the minimum period of time to service the next element
Thread.Sleep(sleepPeriod);
// Grab a safe copy of the element list
LoadRunnerModel[] elements = null;
_pointModelsLock.Read(() => elements = _endpoints);
DateTime now = DateTime.Now;
var pointsReadyToSend = elements.Where
(
point => point.InterlockedRead(() => point.Live && (point.GoLive <= now))
).ToArray();
// Get a list of all the points that are not ready to send
var pointsNotReadyToSend = elements.Except(pointsReadyToSend).ToArray();
// Walk each model - we touch each one inside a lock
// since there can be other threads operating on the model
// including timeouts and returning WCF calls.
pointsReadyToSend.ForEach
(
model =>
{
model.Write
(
() =>
{
// Keep a record of the current model in case
// it throws an exception while we're staging it
currentModel = model;
// Lower the live flag (if we crash calling
// BeginXXX the catch code will re-start us)
model.Live = false;
// Get the step for this model
ScenarioStep step = model.Scenario.Steps.Current;
// This helper enables the scenario watchdog if a
// scenario is just starting
Action StartScenario = () =>
{
if (step.IsFirstStep && !model.Scenario.EnableWatchdog)
{
model.ScenarioStarted = now;
model.Scenario.EnableWatchdog = true;
}
};
// make a connection (if needed)
if (step.UseHook && !model.HookAttached)
{
BeginReceiveEventWindow(model, step.HookMode == ScenarioStep.HookType.Polled);
step.RecordHistory("LoadRunner: Staged Harpoon");
StartScenario();
}
// Send/Receive (if needed)
if (step.ReadyToSend)
{
BeginSendLoop(model);
step.RecordHistory("LoadRunner: Staged SendLoop");
StartScenario();
}
}
);
}
, () => _workerStopSig
);
// Sleep until the next point goes active. Figure out
// the shortest sleep period we have - that's how long
// we'll sleep.
if (pointsNotReadyToSend.Count() > 0)
{
var smallest = pointsNotReadyToSend.Min(ping => ping.GoLive);
sleepPeriod = (smallest > now) ? (int)(smallest - now).TotalMilliseconds : minSleepPeriod;
sleepPeriod = sleepPeriod < 0 ? minSleepPeriod : sleepPeriod;
}
else
sleepPeriod = minSleepPeriod;
}
}
catch (Exception eWorker)
{
// Don't recover if we're shutting down anyway
if (_workerStopSig)
return;
Action RebootDriver = () =>
{
// Reset the point SendLoop that barfed
Stagepoint(true, currentModel);
// Re-boot this thread
ExtensionOfThreadPool.QueueUserWorkItem(Driver);
};
// This means SendLoop barfed
if (eWorker is BeginSendLoopException)
{
Interlocked.Increment(ref _beginHookErrors);
currentModel.Write(() => currentModel.HookAttached = false);
RebootDriver();
}
// This means BeginSendAndReceive barfed
else if (eWorker is BeginSendLoopException)
{
Interlocked.Increment(ref _beginSendLoopErrors);
RebootDriver();
}
// The only kind of exceptions we expect are the
// BeginXXX type. If we made it here something else bad
// happened so allow the worker to die completely.
else
throw;
}
};
// Start the driver thread. This thread will poll the point list
// and keep shoveling them out
ExtensionOfThreadPool.QueueUserWorkItem(Driver);
// Wait for the stop signal
_workerStop.WaitOne();
};
// Start
WorkRunner(Exercise);
}
catch(Exception ex){//not shown}
}
}
Well, it sounds to me like you're the one wanting to make the code more complicated - because you believe your colleagues aren't up to the genuinely simple approach. In many, many cases I find LINQ to Objects makes the code simpler - and yes that does include changing just a few lines to one:
int count = 0;
foreach (Foo f in GenerateFoos())
{
count++;
}
becoming
int count = GenerateFoos().Count();
for example.
Where it isn't making the code simpler, it's fine to try to steer him away from LINQ - but the above is an example where you certainly aren't significantly hampered by avoiding LINQ, but the "KISS" code is clearly the LINQ code.
It sounds like your company could benefit from training up its engineers to take advantage of LINQ to Objects, rather than trying to always appeal to the lowest common denominator.
You seem to be equating Linq to objects with greater complexity, because you assume that unnecessary use of it violates "keep it simple, stupid".
All my experience has been the opposite: it makes complex algorithms much simpler to write and read.
On the contrary, I now regard imperative, statement-based, state-mutational programming as the "risky" option to be used only when really necessary.
So I'd suggest that you put effort into getting more of your colleagues to understand the benefit. It's a false economy to try to limit your approaches to those that you (and others) already understand, because in this industry it pays huge dividends to stay in touch with "new" practises (of course, this stuff is hardly new, but as you point out, it's new to many from a Java or C# 1.x background).
As for trying to pin some charge of "performance issues" on it, I don't think you're going to have much luck. The overhead involved in Linq-to-objects itself is minuscule.

Resources