Skip to content

Commit b324a7f

Browse files
committed
Fix pattern matching in side updates (running/progress/set_props)
1 parent 09252f8 commit b324a7f

File tree

2 files changed

+112
-24
lines changed

2 files changed

+112
-24
lines changed

dash/dash-renderer/src/actions/callbacks.ts

Lines changed: 111 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {
22
concat,
3+
dissoc,
4+
equals,
35
flatten,
46
intersection,
57
keys,
@@ -340,33 +342,108 @@ function updateComponent(component_id: any, props: any) {
340342
};
341343
}
342344

343-
function sideUpdate(outputs: any, dispatch: any) {
344-
toPairs(outputs).forEach(([id, value]) => {
345-
let componentId = id,
346-
propName;
345+
function parsePMCId(id: string) {
346+
let componentId, propName;
347+
const index = id.lastIndexOf('}');
348+
if (index + 2 < id.length) {
349+
propName = id.substring(index + 2);
350+
componentId = JSON.parse(id.substring(0, index + 1));
351+
} else {
352+
componentId = JSON.parse(id);
353+
}
354+
return [componentId, propName];
355+
}
347356

348-
if (id.startsWith('{')) {
349-
const index = id.lastIndexOf('}');
350-
if (index + 2 < id.length) {
351-
propName = id.substring(index + 2);
352-
componentId = JSON.parse(id.substring(0, index + 1));
353-
} else {
354-
componentId = JSON.parse(id);
357+
function sideUpdate(outputs: any, cb: ICallbackPayload) {
358+
return function (dispatch: any, getState: any) {
359+
toPairs(outputs)
360+
.reduce((acc, [id, value], i) => {
361+
let componentId = id,
362+
propName,
363+
replacedIds = [];
364+
365+
if (id.startsWith('{')) {
366+
[componentId, propName] = parsePMCId(id);
367+
replacedIds = replacePMC(componentId, cb, i, getState);
368+
} else if (id.includes('.')) {
369+
[componentId, propName] = id.split('.');
370+
}
371+
372+
const props = propName ? {[propName]: value} : value;
373+
374+
if (replacedIds.length === 0) {
375+
acc.push([componentId, props]);
376+
} else if (replacedIds.length === 1) {
377+
acc.push([replacedIds[0], props]);
378+
} else {
379+
replacedIds.forEach((rep: any) => {
380+
acc.push([rep, props]);
381+
});
382+
}
383+
384+
return acc;
385+
}, [] as any[])
386+
.forEach(([id, idProps]) => {
387+
dispatch(updateComponent(id, idProps));
388+
});
389+
};
390+
}
391+
392+
function getAllPMCIds(id: any, state: any, triggerKey: string) {
393+
const keysOfIds = keys(id);
394+
const idKey = keysOfIds.join(',');
395+
return state.paths.objs[idKey]
396+
.map((obj: any) =>
397+
keysOfIds.reduce((acc, key, i) => {
398+
acc[key] = obj.values[i];
399+
return acc;
400+
}, {} as any)
401+
)
402+
.filter((obj: any) =>
403+
equals(dissoc(triggerKey, obj), dissoc(triggerKey, id))
404+
);
405+
}
406+
407+
function replacePMC(
408+
id: any,
409+
cb: ICallbackPayload,
410+
index: number,
411+
getState: any
412+
) {
413+
let extras: any = [];
414+
const replaced: any = {};
415+
toPairs(id).forEach(([key, value]) => {
416+
if (extras.length) {
417+
// All done.
418+
return;
419+
}
420+
if (Array.isArray(value)) {
421+
const triggerValue = (cb.parsedChangedPropsIds[index] ||
422+
cb.parsedChangedPropsIds[0])[key];
423+
if (value.includes('MATCH')) {
424+
replaced[key] = triggerValue;
425+
} else if (value.includes('ALL')) {
426+
extras = getAllPMCIds(id, getState(), key);
427+
} else if (value.includes('ALLSMALLER')) {
428+
extras = getAllPMCIds(id, getState(), key).filter(
429+
(obj: any) => obj[key] < triggerValue
430+
);
355431
}
356-
} else if (id.includes('.')) {
357-
[componentId, propName] = id.split('.');
432+
} else {
433+
replaced[key] = value;
358434
}
359-
360-
const props = propName ? {[propName]: value} : value;
361-
dispatch(updateComponent(componentId, props));
362435
});
436+
if (extras.length) {
437+
return extras;
438+
}
439+
return [replaced];
363440
}
364441

365442
function handleServerside(
366443
dispatch: any,
367444
hooks: any,
368445
config: any,
369-
payload: any,
446+
payload: ICallbackPayload,
370447
long: LongCallbackInfo | undefined,
371448
additionalArgs: [string, string, boolean?][] | undefined,
372449
getState: any,
@@ -386,7 +463,7 @@ function handleServerside(
386463
let moreArgs = additionalArgs;
387464

388465
if (running) {
389-
sideUpdate(running.running, dispatch);
466+
dispatch(sideUpdate(running.running, payload));
390467
runningOff = running.runningOff;
391468
}
392469

@@ -496,10 +573,10 @@ function handleServerside(
496573
dispatch(removeCallbackJob({jobId: job}));
497574
}
498575
if (runningOff) {
499-
sideUpdate(runningOff, dispatch);
576+
dispatch(sideUpdate(runningOff, payload));
500577
}
501578
if (progressDefault) {
502-
sideUpdate(progressDefault, dispatch);
579+
dispatch(sideUpdate(progressDefault, payload));
503580
}
504581
};
505582

@@ -522,11 +599,11 @@ function handleServerside(
522599
}
523600

524601
if (data.sideUpdate) {
525-
sideUpdate(data.sideUpdate, dispatch);
602+
dispatch(sideUpdate(data.sideUpdate, payload));
526603
}
527604

528605
if (data.progress) {
529-
sideUpdate(data.progress, dispatch);
606+
dispatch(sideUpdate(data.progress, payload));
530607
}
531608
if (!progressDefault && data.progressDefault) {
532609
progressDefault = data.progressDefault;
@@ -671,11 +748,19 @@ export function executeCallback(
671748

672749
const __execute = async (): Promise<CallbackResult> => {
673750
try {
751+
const changedPropIds = keys<string>(cb.changedPropIds);
752+
const parsedChangedPropsIds = changedPropIds.map(propId => {
753+
if (propId.startsWith('{')) {
754+
return parsePMCId(propId)[0];
755+
}
756+
return propId;
757+
});
674758
const payload: ICallbackPayload = {
675759
output,
676760
outputs: isMultiOutputProp(output) ? outputs : outputs[0],
677761
inputs: inVals,
678-
changedPropIds: keys(cb.changedPropIds),
762+
changedPropIds,
763+
parsedChangedPropsIds,
679764
state: cb.callback.state.length
680765
? fillVals(paths, layout, cb, state, 'State')
681766
: undefined
@@ -721,7 +806,9 @@ export function executeCallback(
721806
if (inter.length) {
722807
additionalArgs.push(['cancelJob', job.jobId]);
723808
if (job.progressDefault) {
724-
sideUpdate(job.progressDefault, dispatch);
809+
dispatch(
810+
sideUpdate(job.progressDefault, payload)
811+
);
725812
}
726813
}
727814
}

dash/dash-renderer/src/types/callbacks.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export interface IStoredCallback extends IExecutedCallback {
7272

7373
export interface ICallbackPayload {
7474
changedPropIds: any[];
75+
parsedChangedPropsIds: any[];
7576
inputs: any[];
7677
output: string;
7778
outputs: any[];

0 commit comments

Comments
 (0)