Skip to content

Commit 409d74d

Browse files
authored
fix(schema-compiler): Fix cubes inheritance (#9386)
* some improvements in transformYamlCubeObj() * implement inheritance for preaggs & DAPs * rename prepareCompiler → prepareJsCompiler() in tests * add tests for inheritance * hide sql prop from parent cube if needed * add tests for sql override * fix pre-aggs inheritance * fix test * remove unneeded * add more tests for views and errors * fix preaggs again * mooooore tests :) * more test coverage * more tests for folders * remove useless check * just refactoring * fix schema validation for extended cubes * add tests * update joi to the latest * fix tests * add tests for pre-aggs * fix test * fix tests * fix snapshots after merge changes
1 parent 610820f commit 409d74d

File tree

61 files changed

+3579
-451
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+3579
-451
lines changed

packages/cubejs-api-gateway/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"graphql-tag": "^2.12.6",
4141
"http-proxy-middleware": "^3.0.0",
4242
"inflection": "^1.12.0",
43-
"joi": "^17.8.3",
43+
"joi": "^17.13.3",
4444
"jsonwebtoken": "^9.0.2",
4545
"jwk-to-pem": "^2.0.4",
4646
"moment": "^2.24.0",

packages/cubejs-schema-compiler/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"cron-parser": "^4.9.0",
4848
"humps": "^2.0.1",
4949
"inflection": "^1.12.0",
50-
"joi": "^17.8.3",
50+
"joi": "^17.13.3",
5151
"js-yaml": "^4.1.0",
5252
"lru-cache": "^11.1.0",
5353
"moment-timezone": "^0.5.46",

packages/cubejs-schema-compiler/src/adapter/BaseMeasure.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { UserError } from '../compiler/UserError';
22
import type { BaseQuery } from './BaseQuery';
3-
import { MeasureDefinition } from "../compiler/CubeEvaluator";
3+
import { MeasureDefinition } from '../compiler/CubeEvaluator';
44

55
export class BaseMeasure {
66
public readonly expression: any;

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

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -256,25 +256,17 @@ export class CubeEvaluator extends CubeSymbols {
256256
}
257257

258258
private prepareHierarchies(cube: any, errorReporter: ErrorReporter): void {
259-
const uniqueHierarchyNames = new Set();
260259
if (Object.keys(cube.hierarchies).length) {
261-
cube.evaluatedHierarchies = Object.entries(cube.hierarchies).map(([name, hierarchy]) => {
262-
if (uniqueHierarchyNames.has(name)) {
263-
errorReporter.error(`Duplicate hierarchy name '${name}' in cube '${cube.name}'`);
264-
}
265-
uniqueHierarchyNames.add(name);
266-
267-
return ({
268-
name,
269-
...(typeof hierarchy === 'object' ? hierarchy : {}),
270-
levels: this.evaluateReferences(
271-
cube.name,
272-
// @ts-ignore
273-
hierarchy.levels,
274-
{ originalSorting: true }
275-
)
276-
});
277-
});
260+
cube.evaluatedHierarchies = Object.entries(cube.hierarchies).map(([name, hierarchy]) => ({
261+
name,
262+
...(typeof hierarchy === 'object' ? hierarchy : {}),
263+
levels: this.evaluateReferences(
264+
cube.name,
265+
// @ts-ignore
266+
hierarchy.levels,
267+
{ originalSorting: true }
268+
)
269+
}));
278270
}
279271

280272
if (cube.isView && (cube.includedMembers || []).length) {

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

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,19 @@ export type ToString = { toString(): string };
1414
interface CubeDefinition {
1515
name: string;
1616
extends?: (...args: Array<unknown>) => { __cubeName: string };
17+
sql?: string | (() => string);
18+
// eslint-disable-next-line camelcase
19+
sql_table?: string | (() => string);
20+
sqlTable?: string | (() => string);
1721
measures?: Record<string, any>;
1822
dimensions?: Record<string, any>;
1923
segments?: Record<string, any>;
2024
hierarchies?: Record<string, any>;
2125
preAggregations?: Record<string, any>;
26+
// eslint-disable-next-line camelcase
27+
pre_aggregations?: Record<string, any>;
2228
joins?: Record<string, any>;
23-
accessPolicy?: Record<string, any>;
29+
accessPolicy?: any[];
2430
includes?: any;
2531
excludes?: any;
2632
cubes?: any;
@@ -36,7 +42,7 @@ interface SplitViews {
3642
const FunctionRegex = /function\s+\w+\(([A-Za-z0-9_,]*)|\(([\s\S]*?)\)\s*=>|\(?(\w+)\)?\s*=>/;
3743
export const CONTEXT_SYMBOLS = {
3844
SECURITY_CONTEXT: 'securityContext',
39-
// SECURITY_CONTEXT has been deprecated, however security_context (lowecase)
45+
// SECURITY_CONTEXT has been deprecated, however security_context (lowercase)
4046
// is allowed in RBAC policies for query-time attribute matching
4147
security_context: 'securityContext',
4248
securityContext: 'securityContext',
@@ -106,10 +112,13 @@ export class CubeSymbols {
106112
}
107113

108114
public createCube(cubeDefinition: CubeDefinition) {
115+
let preAggregations: any;
116+
let joins: any;
109117
let measures: any;
110118
let dimensions: any;
111119
let segments: any;
112120
let hierarchies: any;
121+
let accessPolicy: any;
113122

114123
const cubeObject = Object.assign({
115124
allDefinitions(type: string) {
@@ -122,6 +131,37 @@ export class CubeSymbols {
122131
return { ...cubeDefinition[type] };
123132
}
124133
},
134+
135+
get preAggregations() {
136+
// For preAggregations order is important, and destructing parents cube pre-aggs first will lead to
137+
// unexpected results, so we can not use common approach with allDefinitions('preAggregations') here.
138+
if (!preAggregations) {
139+
const parentPreAggregations = cubeDefinition.extends ? super.preAggregations : null;
140+
// Unfortunately, cube is not camelized yet at this point :(
141+
const localPreAggregations = cubeDefinition.preAggregations || cubeDefinition.pre_aggregations;
142+
143+
if (parentPreAggregations) {
144+
preAggregations = { ...localPreAggregations, ...parentPreAggregations, ...localPreAggregations };
145+
} else {
146+
preAggregations = { ...localPreAggregations };
147+
}
148+
}
149+
return preAggregations;
150+
},
151+
set preAggregations(v) {
152+
// Dont allow to modify
153+
},
154+
155+
get joins() {
156+
if (!joins) {
157+
joins = this.allDefinitions('joins');
158+
}
159+
return joins;
160+
},
161+
set joins(v) {
162+
// Dont allow to modify
163+
},
164+
125165
get measures() {
126166
if (!measures) {
127167
measures = this.allDefinitions('measures');
@@ -159,18 +199,41 @@ export class CubeSymbols {
159199
return hierarchies;
160200
},
161201
set hierarchies(v) {
162-
//
202+
// Dont allow to modify
203+
},
204+
205+
get accessPolicy() {
206+
if (!accessPolicy) {
207+
const parentAcls = cubeDefinition.extends ? super.accessPolicy : [];
208+
accessPolicy = [...(parentAcls || []), ...(cubeDefinition.accessPolicy || [])];
209+
}
210+
// Schema validator expects accessPolicy to be not empty if defined
211+
if (accessPolicy.length) {
212+
return accessPolicy;
213+
} else {
214+
return undefined;
215+
}
216+
},
217+
set accessPolicy(v) {
218+
// Dont allow to modify
163219
}
164220
},
165221
cubeDefinition);
166222

167223
if (cubeDefinition.extends) {
168224
const superCube = this.resolveSymbolsCall(cubeDefinition.extends, (name: string) => this.cubeReferenceProxy(name));
169-
Object.setPrototypeOf(
170-
cubeObject,
171-
// eslint-disable-next-line no-underscore-dangle
172-
superCube.__cubeName ? this.getCubeDefinition(superCube.__cubeName) : superCube
173-
);
225+
// eslint-disable-next-line no-underscore-dangle
226+
const parentCube = superCube.__cubeName ? this.getCubeDefinition(superCube.__cubeName) : superCube;
227+
Object.setPrototypeOf(cubeObject, parentCube);
228+
229+
// We have 2 different properties that are mutually exclusive: `sqlTable` & `sql`
230+
// And if in extending cube one of them is defined - we need to hide the other from parent cube definition
231+
// Unfortunately, cube is not camelized yet at this point :(
232+
if ((cubeDefinition.sqlTable || cubeDefinition.sql_table) && parentCube.sql) {
233+
cubeObject.sql = undefined;
234+
} else if (cubeDefinition.sql && (parentCube.sqlTable || parentCube.sql_table)) {
235+
cubeObject.sqlTable = undefined;
236+
}
174237
}
175238

176239
return cubeObject;

0 commit comments

Comments
 (0)