Skip to content

Commit 5d5f7da

Browse files
authored
fix(client-vue3): Avoid Maximum recursive updates exceeded
1 parent c3cd3a6 commit 5d5f7da

File tree

2 files changed

+127
-3
lines changed

2 files changed

+127
-3
lines changed

packages/cubejs-client-vue3/src/QueryBuilder.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -381,19 +381,27 @@ export default {
381381
};
382382

383383
this.chartType = chartType || this.chartType;
384-
this.pivotConfig = ResultSet.getNormalizedPivotConfig(
384+
let pivot = ResultSet.getNormalizedPivotConfig(
385385
validatedQuery,
386386
pivotConfig !== undefined ? pivotConfig : this.pivotConfig
387387
);
388-
this.copyQueryFromProps(validatedQuery);
388+
if (!equals(pivot, this.pivotConfig)) {
389+
this.pivotConfig = pivot;
390+
}
391+
392+
if (!areQueriesEqual(this.prevValidatedQuery, validatedQuery)) {
393+
this.copyQueryFromProps(validatedQuery);
394+
}
389395
}
390396

391397
// query heuristics should only apply on query change (not applied to the initial query)
392398
if (this.prevValidatedQuery !== null) {
393399
this.skipHeuristics = false;
394400
}
395401

396-
this.prevValidatedQuery = validatedQuery;
402+
if (!areQueriesEqual(this.prevValidatedQuery, validatedQuery)) {
403+
this.prevValidatedQuery = validatedQuery;
404+
}
397405
return validatedQuery;
398406
},
399407
},

packages/cubejs-client-vue3/tests/unit/QueryBuilder.spec.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,80 @@ describe('QueryBuilder.vue', () => {
928928
expect(wrapper.vm.pivotConfig).toEqual(expectedPivotForLine);
929929
expect(wrapper.vm.chartType).toBe('line');
930930
});
931+
it('should not reassign validatedQuery if it has not changed', async () => {
932+
const cube = createCubeApi();
933+
jest
934+
.spyOn(cube, 'request')
935+
.mockImplementation(fetchMock(load))
936+
.mockImplementationOnce(fetchMock(meta));
937+
938+
const wrapper = shallowMount(QueryBuilder, {
939+
propsData: {
940+
cubeApi: cube,
941+
query: {
942+
measures: ['Orders.count'],
943+
timeDimensions: [
944+
{
945+
dimension: 'Orders.createdAt',
946+
},
947+
],
948+
},
949+
},
950+
});
951+
952+
const initialValidatedQuery = {
953+
measures: ['measure1'],
954+
dimensions: ['dimension1'],
955+
};
956+
wrapper.setData({ prevValidatedQuery: initialValidatedQuery });
957+
958+
wrapper.setData({
959+
measures: [{ name: 'measure1' }],
960+
dimensions: [{ name: 'dimension1' }],
961+
});
962+
963+
await wrapper.vm.$nextTick();
964+
965+
expect(wrapper.vm.prevValidatedQuery.measures).toEqual(initialValidatedQuery.measures);
966+
expect(wrapper.vm.prevValidatedQuery.dimensions).toEqual(initialValidatedQuery.dimensions);
967+
});
968+
969+
it('should reassign validatedQuery if it has changed', async () => {
970+
const cube = createCubeApi();
971+
jest
972+
.spyOn(cube, 'request')
973+
.mockImplementation(fetchMock(load))
974+
.mockImplementationOnce(fetchMock(meta));
975+
976+
const wrapper = shallowMount(QueryBuilder, {
977+
propsData: {
978+
cubeApi: cube,
979+
query: {
980+
measures: ['Orders.count'],
981+
timeDimensions: [
982+
{
983+
dimension: 'Orders.createdAt',
984+
},
985+
],
986+
},
987+
},
988+
});
989+
const initialValidatedQuery = {
990+
measures: ['measure1'],
991+
dimensions: ['dimension1'],
992+
};
993+
wrapper.setData({ prevValidatedQuery: initialValidatedQuery });
994+
995+
wrapper.setData({
996+
measures: [{ name: 'measure2' }],
997+
dimensions: [{ name: 'dimension1' }],
998+
});
999+
1000+
await wrapper.vm.$nextTick();
1001+
1002+
expect(wrapper.vm.prevValidatedQuery.measures).not.toEqual(initialValidatedQuery.measures);
1003+
expect(wrapper.vm.prevValidatedQuery.dimensions).toEqual(initialValidatedQuery.dimensions);
1004+
});
9311005
});
9321006
describe('orderMembers', () => {
9331007
it('does not contain time dimension if granularity is set to none', async () => {
@@ -1001,6 +1075,48 @@ describe('QueryBuilder.vue', () => {
10011075
])
10021076
);
10031077
});
1078+
it('calls copyQueryFromProps if query is changed', async () => {
1079+
const cube = createCubeApi();
1080+
jest
1081+
.spyOn(cube, 'request')
1082+
.mockImplementation(fetchMock(load))
1083+
.mockImplementationOnce(fetchMock(meta));
1084+
1085+
const copyQueryFromProps = jest.spyOn(QueryBuilder.methods, 'copyQueryFromProps');
1086+
1087+
const wrapper = shallowMount(QueryBuilder, {
1088+
propsData: {
1089+
cubeApi: cube,
1090+
query: {
1091+
measures: ['Orders.count'],
1092+
timeDimensions: [
1093+
{
1094+
dimension: 'Orders.createdAt',
1095+
},
1096+
],
1097+
},
1098+
},
1099+
});
1100+
await flushPromises();
1101+
1102+
await wrapper.vm.$nextTick();
1103+
expect(wrapper.vm.measures.length).toEqual(1);
1104+
expect(wrapper.vm.measures[0].name).toEqual('Orders.count');
1105+
1106+
await wrapper.vm.$nextTick();
1107+
1108+
expect(copyQueryFromProps).toHaveBeenCalled();
1109+
expect(copyQueryFromProps).toHaveBeenCalledTimes(1);
1110+
const initialValidatedQuery = {
1111+
measures: ['measure1'],
1112+
dimensions: ['dimension1'],
1113+
};
1114+
wrapper.setData({ prevValidatedQuery: initialValidatedQuery });
1115+
await wrapper.vm.$nextTick();
1116+
1117+
expect(copyQueryFromProps).toHaveBeenCalledTimes(2);
1118+
copyQueryFromProps.mockRestore();
1119+
});
10041120
});
10051121
});
10061122
});

0 commit comments

Comments
 (0)