Skip to content

Commit 15af478

Browse files
committed
feat(schema-compiler): Support folders via views extends
1 parent 2436f45 commit 15af478

File tree

6 files changed

+136
-64
lines changed

6 files changed

+136
-64
lines changed

packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,9 @@ export class CubeEvaluator extends CubeSymbols {
224224
}
225225

226226
private prepareFolders(cube: any, errorReporter: ErrorReporter) {
227-
if (Array.isArray(cube.folders)) {
228-
cube.folders = cube.folders.map(it => {
227+
const folders = cube.rawFolders();
228+
if (folders.length) {
229+
cube.folders = folders.map(it => {
229230
const includedMembers = this.allMembersOrList(cube, it.includes);
230231
const includes = includedMembers.map(memberName => {
231232
if (memberName.includes('.')) {

packages/cubejs-schema-compiler/src/compiler/CubeSymbols.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface CubeDefinition {
2626
pre_aggregations?: Record<string, any>;
2727
joins?: Record<string, any>;
2828
accessPolicy?: any[];
29+
folders?: any[];
2930
includes?: any;
3031
excludes?: any;
3132
cubes?: any;
@@ -118,6 +119,7 @@ export class CubeSymbols {
118119
let segments: any;
119120
let hierarchies: any;
120121
let accessPolicy: any;
122+
let folders: any;
121123

122124
const cubeObject = Object.assign({
123125
allDefinitions(type: string) {
@@ -131,6 +133,22 @@ export class CubeSymbols {
131133
}
132134
},
133135

136+
// Folders are not a part of Cube Symbols and are constructed in the CubeEvaluator,
137+
// but views can extend other views, so we need the ability to access parent's folders.
138+
rawFolders() {
139+
if (!folders) {
140+
if (cubeDefinition.extends) {
141+
folders = [
142+
...super.rawFolders(),
143+
...(cubeDefinition.folders || [])
144+
];
145+
} else {
146+
folders = [...(cubeDefinition.folders || [])];
147+
}
148+
}
149+
return folders;
150+
},
151+
134152
get preAggregations() {
135153
// For preAggregations order is important, and destructing parents cube pre-aggs first will lead to
136154
// unexpected results, so we can not use common approach with allDefinitions('preAggregations') here.

packages/cubejs-schema-compiler/src/compiler/CubeValidator.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,8 @@ const baseSchema = {
745745
refreshKey: CubeRefreshKeySchema,
746746
fileName: Joi.string().required(),
747747
extends: Joi.func(),
748-
allDefinitions: Joi.func(),
748+
allDefinitions: Joi.func(), // Helpers function for extending
749+
rawFolders: Joi.func(), // Helpers function for extending
749750
title: Joi.string(),
750751
sqlAlias: Joi.string(),
751752
dataSource: Joi.string(),

packages/cubejs-schema-compiler/test/unit/__snapshots__/schema.test.ts.snap

Lines changed: 62 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,67 +1348,6 @@ Array [
13481348
]
13491349
`;
13501350

1351-
exports[`Schema Testing Views extends custom granularities and timeshifts 1`] = `
1352-
Object {
1353-
"aliasMember": "orders.createdAt",
1354-
"description": undefined,
1355-
"format": undefined,
1356-
"granularities": Object {
1357-
"half_year": Object {
1358-
"interval": "6 months",
1359-
"title": "6 month intervals",
1360-
},
1361-
"half_year_by_1st_april": Object {
1362-
"interval": "6 months",
1363-
"offset": "3 months",
1364-
"title": "Half year from Apr to Oct",
1365-
},
1366-
"half_year_by_1st_june": Object {
1367-
"interval": "6 months",
1368-
"origin": "2020-06-01 10:00:00",
1369-
},
1370-
"half_year_by_1st_march": Object {
1371-
"interval": "6 months",
1372-
"origin": "2020-03-01",
1373-
},
1374-
},
1375-
"meta": undefined,
1376-
"ownedByCube": false,
1377-
"sql": [Function],
1378-
"title": undefined,
1379-
"type": "time",
1380-
}
1381-
`;
1382-
1383-
exports[`Schema Testing Views extends custom granularities and timeshifts 2`] = `
1384-
Object {
1385-
"aggType": "count",
1386-
"aliasMember": "orders.count_shifted_year",
1387-
"description": undefined,
1388-
"format": undefined,
1389-
"meta": undefined,
1390-
"multiStage": true,
1391-
"ownedByCube": false,
1392-
"sql": [Function],
1393-
"timeShift": Array [
1394-
Object {
1395-
"interval": "1 year",
1396-
"timeDimension": [Function],
1397-
"type": "prior",
1398-
},
1399-
],
1400-
"timeShiftReferences": Array [
1401-
Object {
1402-
"interval": "1 year",
1403-
"timeDimension": "createdAt",
1404-
"type": "prior",
1405-
},
1406-
],
1407-
"title": undefined,
1408-
"type": "number",
1409-
}
1410-
`;
1411-
14121351
exports[`Schema Testing Views allows to override \`title\`, \`description\`, \`meta\`, and \`format\` on includes members 1`] = `
14131352
Object {
14141353
"accessPolicy": undefined,
@@ -1570,6 +1509,68 @@ Object {
15701509
},
15711510
"name": "orders_view",
15721511
"preAggregations": Object {},
1512+
"rawFolders": [Function],
15731513
"segments": Object {},
15741514
}
15751515
`;
1516+
1517+
exports[`Schema Testing Views extends custom granularities and timeshifts 1`] = `
1518+
Object {
1519+
"aliasMember": "orders.createdAt",
1520+
"description": undefined,
1521+
"format": undefined,
1522+
"granularities": Object {
1523+
"half_year": Object {
1524+
"interval": "6 months",
1525+
"title": "6 month intervals",
1526+
},
1527+
"half_year_by_1st_april": Object {
1528+
"interval": "6 months",
1529+
"offset": "3 months",
1530+
"title": "Half year from Apr to Oct",
1531+
},
1532+
"half_year_by_1st_june": Object {
1533+
"interval": "6 months",
1534+
"origin": "2020-06-01 10:00:00",
1535+
},
1536+
"half_year_by_1st_march": Object {
1537+
"interval": "6 months",
1538+
"origin": "2020-03-01",
1539+
},
1540+
},
1541+
"meta": undefined,
1542+
"ownedByCube": false,
1543+
"sql": [Function],
1544+
"title": undefined,
1545+
"type": "time",
1546+
}
1547+
`;
1548+
1549+
exports[`Schema Testing Views extends custom granularities and timeshifts 2`] = `
1550+
Object {
1551+
"aggType": "count",
1552+
"aliasMember": "orders.count_shifted_year",
1553+
"description": undefined,
1554+
"format": undefined,
1555+
"meta": undefined,
1556+
"multiStage": true,
1557+
"ownedByCube": false,
1558+
"sql": [Function],
1559+
"timeShift": Array [
1560+
Object {
1561+
"interval": "1 year",
1562+
"timeDimension": [Function],
1563+
"type": "prior",
1564+
},
1565+
],
1566+
"timeShiftReferences": Array [
1567+
Object {
1568+
"interval": "1 year",
1569+
"timeDimension": "createdAt",
1570+
"type": "prior",
1571+
},
1572+
],
1573+
"title": undefined,
1574+
"type": "number",
1575+
}
1576+
`;

packages/cubejs-schema-compiler/test/unit/fixtures/folders.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,20 @@ views:
9090
- users_age
9191
- users_state
9292
- renamed_orders_status
93+
- name: test_view3
94+
extends: test_view2
95+
cubes:
96+
- join_path: users
97+
prefix: true
98+
includes:
99+
- city
100+
- gender
101+
folders:
102+
- name: folder2
103+
includes:
104+
- users_city
105+
- users_gender
106+
93107
# - name: empty_view
94108
# cubes:
95109
# - join_path: orders

packages/cubejs-schema-compiler/test/unit/folders.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,43 @@ describe('Cube Folders', () => {
4343
);
4444
});
4545

46+
it('folders from view extending other view', async () => {
47+
const view2 = metaTransformer.cubes.find(
48+
(it) => it.config.name === 'test_view2'
49+
);
50+
const view3 = metaTransformer.cubes.find(
51+
(it) => it.config.name === 'test_view3'
52+
);
53+
54+
expect(view2.config.folders.length).toBe(1);
55+
expect(view3.config.folders.length).toBe(2);
56+
57+
const folder1 = view2.config.folders.find(
58+
(it) => it.name === 'folder1'
59+
);
60+
expect(folder1.members).toEqual([
61+
'test_view2.users_age',
62+
'test_view2.users_state',
63+
'test_view2.renamed_orders_status',
64+
]);
65+
66+
const folder1v3 = view3.config.folders.find(
67+
(it) => it.name === 'folder1'
68+
);
69+
expect(folder1v3.members).toEqual([
70+
'test_view3.users_age',
71+
'test_view3.users_state',
72+
'test_view3.renamed_orders_status',
73+
]);
74+
75+
const folder2 = view3.config.folders.find(
76+
(it) => it.name === 'folder2'
77+
);
78+
expect(folder2.members).toEqual(
79+
expect.arrayContaining(['test_view3.users_city', 'test_view3.users_gender'])
80+
);
81+
});
82+
4683
it('throws errors for folder members with path', async () => {
4784
const modelContent = fs.readFileSync(
4885
path.join(process.cwd(), '/test/unit/fixtures/folders_invalid_path.yml'),

0 commit comments

Comments
 (0)