Skip to content

Commit da92f16

Browse files
committed
[FIX] CorePlugins: Prevent dispatch during adaptRange
The method `adaptRange` was originally designed for each plugin to update their own ranges, whithout having to manually react to the different commands that alter the sheets structure. We have abused the system when we introduced the spreadsheet pivots to allow the modification of a pivot dataset (which is stored in PivotCorePlugin) from another entry point `SpreadsheetPivotCorePlugin`) by dispatching an UPDATE_PIVOT command but there are some side effects that we did not account for, the dispatched command is forwarded to EVERY other plugin. Meaning that during an adaptRange, which should only concern a specific plugin, we end up notifying a change to the whole core/coreview stack. This revision removes the access to 'dispatch' during the handling of `adaptRange` so we ensure that the changes are kept locally. Task: 5380747
1 parent 7ed20c4 commit da92f16

File tree

4 files changed

+94
-62
lines changed

4 files changed

+94
-62
lines changed

src/helpers/pivot/pivot_registry.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ModelConfig } from "../../model";
22
import { Registry } from "../../registries/registry";
3-
import { CoreGetters, Getters } from "../../types";
3+
import { ApplyRangeChange, CoreGetters, Getters, Range } from "../../types";
44
import { PivotCoreDefinition, PivotField, PivotFields } from "../../types/pivot";
55
import { Pivot } from "../../types/pivot_runtime";
66
import { PivotRuntimeDefinition } from "./pivot_runtime_definition";
@@ -32,6 +32,11 @@ export interface PivotRegistryItem {
3232
datetimeGranularities: string[];
3333
isMeasureCandidate: (field: PivotField) => boolean;
3434
isGroupable: (field: PivotField) => boolean;
35+
adaptRanges?: (
36+
getters: CoreGetters,
37+
definition: PivotCoreDefinition,
38+
applyChange: ApplyRangeChange
39+
) => PivotCoreDefinition;
3540
}
3641

3742
export const pivotRegistry = new Registry<PivotRegistryItem>();
@@ -54,4 +59,43 @@ pivotRegistry.add("SPREADSHEET", {
5459
datetimeGranularities: [...dateGranularities, "hour_number", "minute_number", "second_number"],
5560
isMeasureCandidate: (field: PivotField) => !["datetime", "boolean"].includes(field.type),
5661
isGroupable: () => true,
62+
adaptRanges: (getters, definition, applyChange) => {
63+
if (definition.type !== "SPREADSHEET" || !definition.dataSet) {
64+
return definition;
65+
}
66+
// if (definition.dataSet) {
67+
const { sheetId, zone } = definition.dataSet;
68+
const range = getters.getRangeFromZone(sheetId, zone);
69+
const adaptedRange = adaptPivotRange(range, applyChange);
70+
71+
if (adaptedRange === range) {
72+
return definition;
73+
}
74+
75+
const dataSet = adaptedRange && {
76+
sheetId: adaptedRange.sheetId,
77+
zone: adaptedRange.zone,
78+
};
79+
return { ...definition, dataSet };
80+
// this.dispatch("UPDATE_PIVOT", { pivotId, pivot: { ...definition, dataSet } });
81+
// }
82+
},
5783
});
84+
85+
function adaptPivotRange(
86+
range: Range | undefined,
87+
applyChange: ApplyRangeChange
88+
): Range | undefined {
89+
if (!range) {
90+
return undefined;
91+
}
92+
const change = applyChange(range);
93+
switch (change.changeType) {
94+
case "NONE":
95+
return range;
96+
case "REMOVE":
97+
return undefined;
98+
default:
99+
return change.range;
100+
}
101+
}

src/plugins/core/pivot.ts

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { compile } from "../../formulas";
22
import { deepCopy, deepEquals } from "../../helpers";
33
import { createPivotFormula, getMaxObjectId } from "../../helpers/pivot/pivot_helpers";
4+
import { pivotRegistry } from "../../helpers/pivot/pivot_registry";
45
import { SpreadsheetPivotTable } from "../../helpers/pivot/table_spreadsheet_pivot";
56
import {
67
ApplyRangeChange,
@@ -132,6 +133,18 @@ export class PivotCorePlugin extends CorePlugin<CoreState> implements CoreState
132133
}
133134

134135
adaptRanges(applyChange: ApplyRangeChange) {
136+
for (const pivotId in this.pivots) {
137+
const definition = deepCopy(this.pivots[pivotId]?.definition);
138+
if (!definition) {
139+
continue;
140+
}
141+
const newDefinition = pivotRegistry
142+
.get(definition.type)
143+
?.adaptRanges?.(this.getters, definition, applyChange);
144+
if (newDefinition && !deepEquals(definition, newDefinition)) {
145+
this.history.update("pivots", pivotId, "definition", newDefinition);
146+
}
147+
}
135148
for (const sheetId in this.compiledMeasureFormulas) {
136149
for (const formulaString in this.compiledMeasureFormulas[sheetId]) {
137150
const compiledFormula = this.compiledMeasureFormulas[sheetId][formulaString];
@@ -314,21 +327,37 @@ export class PivotCorePlugin extends CorePlugin<CoreState> implements CoreState
314327
if (!pivot) {
315328
continue;
316329
}
317-
const def = deepCopy(pivot.definition);
318330

319-
for (const measure of def.measures) {
331+
for (const measure of pivot.definition.measures) {
320332
if (measure.computedBy?.formula === formulaString) {
321-
const measureIndex = def.measures.indexOf(measure);
322-
if (measureIndex !== -1) {
323-
def.measures[measureIndex].computedBy = {
324-
formula: newFormulaString,
325-
sheetId,
326-
};
327-
}
328-
329-
this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
333+
const measureIndex = pivot.definition.measures.indexOf(measure);
334+
this.history.update(
335+
"pivots",
336+
pivotId,
337+
"definition",
338+
"measures",
339+
measureIndex,
340+
"computedBy",
341+
{ formula: newFormulaString, sheetId }
342+
);
330343
}
331344
}
345+
346+
// const def = deepCopy(pivot.definition);
347+
348+
// for (const measure of def.measures) {
349+
// if (measure.computedBy?.formula === formulaString) {
350+
// const measureIndex = def.measures.indexOf(measure);
351+
// if (measureIndex !== -1) {
352+
// def.measures[measureIndex].computedBy = {
353+
// formula: newFormulaString,
354+
// sheetId,
355+
// };
356+
// }
357+
358+
// this.dispatch("UPDATE_PIVOT", { pivotId, pivot: def });
359+
// }
360+
// }
332361
}
333362
}
334363

src/plugins/core/spreadsheet_pivot.ts

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,7 @@
11
import { isZoneValid } from "../../helpers";
2-
import {
3-
ApplyRangeChange,
4-
CommandResult,
5-
CoreCommand,
6-
PivotCoreDefinition,
7-
Range,
8-
} from "../../types";
2+
import { CommandResult, CoreCommand, PivotCoreDefinition } from "../../types";
93
import { CorePlugin } from "../core_plugin";
104

11-
function adaptPivotRange(
12-
range: Range | undefined,
13-
applyChange: ApplyRangeChange
14-
): Range | undefined {
15-
if (!range) {
16-
return undefined;
17-
}
18-
const change = applyChange(range);
19-
switch (change.changeType) {
20-
case "NONE":
21-
return range;
22-
case "REMOVE":
23-
return undefined;
24-
default:
25-
return change.range;
26-
}
27-
}
28-
295
export class SpreadsheetPivotCorePlugin extends CorePlugin {
306
allowDispatch(cmd: CoreCommand) {
317
switch (cmd.type) {
@@ -37,30 +13,6 @@ export class SpreadsheetPivotCorePlugin extends CorePlugin {
3713
return CommandResult.Success;
3814
}
3915

40-
adaptRanges(applyChange: ApplyRangeChange) {
41-
for (const pivotId of this.getters.getPivotIds()) {
42-
const definition = this.getters.getPivotCoreDefinition(pivotId);
43-
if (definition.type !== "SPREADSHEET") {
44-
continue;
45-
}
46-
if (definition.dataSet) {
47-
const { sheetId, zone } = definition.dataSet;
48-
const range = this.getters.getRangeFromZone(sheetId, zone);
49-
const adaptedRange = adaptPivotRange(range, applyChange);
50-
51-
if (adaptedRange === range) {
52-
return;
53-
}
54-
55-
const dataSet = adaptedRange && {
56-
sheetId: adaptedRange.sheetId,
57-
zone: adaptedRange.zone,
58-
};
59-
this.dispatch("UPDATE_PIVOT", { pivotId, pivot: { ...definition, dataSet } });
60-
}
61-
}
62-
}
63-
6416
private checkDataSetValidity(definition: PivotCoreDefinition) {
6517
if (definition.type === "SPREADSHEET" && definition.dataSet) {
6618
const { zone, sheetId } = definition.dataSet;

src/plugins/core_plugin.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ export class CorePlugin<State = any>
5151
uuidGenerator,
5252
}: CorePluginConfig) {
5353
super(stateObserver, dispatch, canDispatch);
54-
range.addRangeProvider(this.adaptRanges.bind(this));
54+
55+
// Bind adaptRanges to a version of `this` where `dispatch` always throws
56+
const thisWithThrowingDispatch = Object.create(this);
57+
thisWithThrowingDispatch.dispatch = () => {
58+
throw new Error("dispatch is not allowed in adaptRanges context");
59+
};
60+
range.addRangeProvider(this.adaptRanges.bind(thisWithThrowingDispatch));
61+
5562
this.getters = getters;
5663
this.uuidGenerator = uuidGenerator;
5764
}

0 commit comments

Comments
 (0)