Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cubejs-schema-compiler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@types/babel__traverse": "^7.20.5",
"@types/inflection": "^1.5.28",
"@types/jest": "^29",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20",
"@types/node-dijkstra": "^2.5.6",
"@types/ramda": "^0.27.34",
Expand Down
89 changes: 36 additions & 53 deletions packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
import R from 'ramda';

import {
AccessPolicyDefinition,
CubeDefinitionExtended,
CubeSymbols,
HierarchyDefinition, JoinDefinition,
PreAggregationDefinition, PreAggregationDefinitionRollup,
HierarchyDefinition,
JoinDefinition,
PreAggregationDefinition,
PreAggregationDefinitionRollup,
type ToString
} from './CubeSymbols';
import { UserError } from './UserError';
Expand Down Expand Up @@ -110,30 +113,6 @@ export type EvaluatedHierarchy = {
[key: string]: any;
};

export type Filter =
| {
member: string;
memberReference?: string;
[key: string]: any;
}
| {
and?: Filter[];
or?: Filter[];
[key: string]: any;
};

export type AccessPolicy = {
rowLevel?: {
filters: Filter[];
};
memberLevel?: {
includes?: string | string[];
excludes?: string | string[];
includesMembers?: string[];
excludesMembers?: string[];
};
};

export type EvaluatedFolder = {
name: string;
includes: (EvaluatedFolder | DimensionDefinition | MeasureDefinition)[];
Expand All @@ -145,15 +124,15 @@ export type EvaluatedCube = {
measures: Record<string, MeasureDefinition>;
dimensions: Record<string, DimensionDefinition>;
segments: Record<string, SegmentDefinition>;
joins: Record<string, JoinDefinition>;
joins: JoinDefinition[];
hierarchies: Record<string, HierarchyDefinition>;
evaluatedHierarchies: EvaluatedHierarchy[];
preAggregations: Record<string, PreAggregationDefinitionExtended>;
dataSource?: string;
folders: EvaluatedFolder[];
sql?: (...args: any[]) => string;
sqlTable?: (...args: any[]) => string;
accessPolicy?: AccessPolicy[];
accessPolicy?: AccessPolicyDefinition[];
};

export class CubeEvaluator extends CubeSymbols {
Expand Down Expand Up @@ -200,7 +179,7 @@ export class CubeEvaluator extends CubeSymbols {
);
}

protected prepareCube(cube, errorReporter: ErrorReporter) {
protected prepareCube(cube, errorReporter: ErrorReporter): EvaluatedCube {
this.prepareJoins(cube, errorReporter);
this.preparePreAggregations(cube, errorReporter);
this.prepareMembers(cube.measures, cube, errorReporter);
Expand Down Expand Up @@ -444,35 +423,39 @@ export class CubeEvaluator extends CubeSymbols {
}

protected prepareJoins(cube: any, _errorReporter: ErrorReporter) {
if (cube.joins) {
// eslint-disable-next-line no-restricted-syntax
for (const join of Object.values(cube.joins) as any[]) {
// eslint-disable-next-line default-case
switch (join.relationship) {
case 'belongs_to':
case 'many_to_one':
case 'manyToOne':
join.relationship = 'belongsTo';
break;
case 'has_many':
case 'one_to_many':
case 'oneToMany':
join.relationship = 'hasMany';
break;
case 'has_one':
case 'one_to_one':
case 'oneToOne':
join.relationship = 'hasOne';
break;
}
}
if (!cube.joins) {
return;
}

const joins: JoinDefinition[] = Array.isArray(cube.joins) ? cube.joins : Object.values(cube.joins);

joins.forEach(join => {
// eslint-disable-next-line default-case
switch (join.relationship) {
case 'belongs_to':
case 'many_to_one':
case 'manyToOne':
join.relationship = 'belongsTo';
break;
case 'has_many':
case 'one_to_many':
case 'oneToMany':
join.relationship = 'hasMany';
break;
case 'has_one':
case 'one_to_one':
case 'oneToOne':
join.relationship = 'hasOne';
break;
}
});
}

protected preparePreAggregations(cube: any, errorReporter: ErrorReporter) {
if (cube.preAggregations) {
// eslint-disable-next-line no-restricted-syntax
for (const preAggregation of Object.values(cube.preAggregations) as any) {
// preAggregation is actually (PreAggregationDefinitionRollup | PreAggregationDefinitionOriginalSql)
if (preAggregation.timeDimension) {
preAggregation.timeDimensionReference = preAggregation.timeDimension;
delete preAggregation.timeDimension;
Expand Down Expand Up @@ -574,7 +557,7 @@ export class CubeEvaluator extends CubeSymbols {
}
}

public cubesByFileName(fileName) {
public cubesByFileName(fileName): CubeDefinitionExtended[] {
return this.byFileName[fileName] || [];
}

Expand Down Expand Up @@ -691,7 +674,7 @@ export class CubeEvaluator extends CubeSymbols {
return this.preAggregations({ scheduled: true });
}

public cubeNames() {
public cubeNames(): string[] {
return Object.keys(this.evaluatedCubes);
}

Expand Down
91 changes: 56 additions & 35 deletions packages/cubejs-schema-compiler/src/compiler/CubeSymbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,35 @@ export type PreAggregationDefinitionRollup = BasePreAggregationDefinition & {
export type PreAggregationDefinition = PreAggregationDefinitionRollup;

export type JoinDefinition = {
name: string,
relationship: string,
sql: (...args: any[]) => string,
};

export type Filter =
| {
member: string;
memberReference?: string;
[key: string]: any;
}
| {
and?: Filter[];
or?: Filter[];
[key: string]: any;
};

export type AccessPolicyDefinition = {
rowLevel?: {
filters: Filter[];
};
memberLevel?: {
includes?: string | string[];
excludes?: string | string[];
includesMembers?: string[];
excludesMembers?: string[];
};
};

export interface CubeDefinition {
name: string;
extends?: (...args: Array<unknown>) => { __cubeName: string };
Expand All @@ -119,8 +144,8 @@ export interface CubeDefinition {
preAggregations?: Record<string, PreAggregationDefinitionRollup | PreAggregationDefinitionOriginalSql>;
// eslint-disable-next-line camelcase
pre_aggregations?: Record<string, PreAggregationDefinitionRollup | PreAggregationDefinitionOriginalSql>;
joins?: Record<string, JoinDefinition>;
accessPolicy?: any[];
joins?: JoinDefinition[];
accessPolicy?: AccessPolicyDefinition[];
// eslint-disable-next-line camelcase
access_policy?: any[];
folders?: any[];
Expand Down Expand Up @@ -221,17 +246,17 @@ export class CubeSymbols {
}

public createCube(cubeDefinition: CubeDefinition): CubeDefinitionExtended {
let preAggregations: any;
let joins: any;
let measures: any;
let dimensions: any;
let segments: any;
let hierarchies: any;
let accessPolicy: any;
let folders: any;
let cubes: any;

const cubeObject = Object.assign({
let preAggregations: CubeDefinition['preAggregations'];
let joins: CubeDefinition['joins'];
let measures: CubeDefinition['measures'];
let dimensions: CubeDefinition['dimensions'];
let segments: CubeDefinition['segments'];
let hierarchies: CubeDefinition['hierarchies'];
let accessPolicy: CubeDefinition['accessPolicy'];
let folders: CubeDefinition['folders'];
let cubes: CubeDefinition['cubes'];

const cubeObject: CubeDefinitionExtended = Object.assign({
allDefinitions(type: string) {
if (cubeDefinition.extends) {
return {
Expand Down Expand Up @@ -297,7 +322,8 @@ export class CubeSymbols {

get joins() {
if (!joins) {
joins = this.allDefinitions('joins');
const parentJoins = cubeDefinition.extends ? super.joins : [];
joins = [...parentJoins, ...(cubeDefinition.joins || [])];
}
return joins;
},
Expand Down Expand Up @@ -383,9 +409,10 @@ export class CubeSymbols {
return cubeObject;
}

protected transform(cubeName: string, errorReporter: ErrorReporter, splitViews: SplitViews) {
protected transform(cubeName: string, errorReporter: ErrorReporter, splitViews: SplitViews): CubeSymbolsDefinition {
const cube = this.getCubeDefinition(cubeName);
const duplicateNames = R.compose(
// @ts-ignore
const duplicateNames: string[] = R.compose(
R.map((nameToDefinitions: any) => nameToDefinitions[0]),
R.toPairs,
R.filter((definitionsByName: any) => definitionsByName.length > 1),
Expand All @@ -397,9 +424,7 @@ export class CubeSymbols {
// @ts-ignore
)([cube.measures, cube.dimensions, cube.segments, cube.preAggregations, cube.hierarchies]);

// @ts-ignore
if (duplicateNames.length > 0) {
// @ts-ignore
errorReporter.error(`${duplicateNames.join(', ')} defined more than once`);
}

Expand Down Expand Up @@ -427,23 +452,24 @@ export class CubeSymbols {
...cube.dimensions || {},
...cube.segments || {},
...cube.preAggregations || {}
};
} as CubeSymbolsDefinition;
}

private camelCaseTypes(obj: Object | undefined) {
private camelCaseTypes(obj: Object | Array<any> | undefined) {
if (!obj) {
return;
}

// eslint-disable-next-line no-restricted-syntax
for (const member of Object.values(obj)) {
const members = Array.isArray(obj) ? obj : Object.values(obj);

members.forEach(member => {
if (member.type && member.type.indexOf('_') !== -1) {
member.type = camelize(member.type, true);
}
if (member.relationship && member.relationship.indexOf('_') !== -1) {
member.relationship = camelize(member.relationship, true);
}
}
});
}

protected transformPreAggregations(preAggregations: Object) {
Expand Down Expand Up @@ -548,7 +574,7 @@ export class CubeSymbols {
}
}

const includeMembers = this.generateIncludeMembers(cubeIncludes, cube.name, type);
const includeMembers = this.generateIncludeMembers(cubeIncludes, type);
this.applyIncludeMembers(includeMembers, cube, type, errorReporter);

const existing = cube.includedMembers ?? [];
Expand Down Expand Up @@ -699,11 +725,7 @@ export class CubeSymbols {
splitViewDef = splitViews[viewName];
}

const includeMembers = this.generateIncludeMembers(
finalIncludes,
parentCube.name,
type
);
const includeMembers = this.generateIncludeMembers(finalIncludes, type);
this.applyIncludeMembers(includeMembers, splitViewDef, type, errorReporter);
} else {
for (const member of finalIncludes) {
Expand Down Expand Up @@ -733,7 +755,7 @@ export class CubeSymbols {
return this.symbols[cubeName]?.cubeObj()?.[type]?.[memberName];
}

protected generateIncludeMembers(members: any[], cubeName: string, type: string) {
protected generateIncludeMembers(members: any[], type: string) {
return members.map(memberRef => {
const path = memberRef.member.split('.');
const resolvedMember = this.getResolvedMember(type, path[path.length - 2], path[path.length - 1]);
Expand Down Expand Up @@ -870,9 +892,8 @@ export class CubeSymbols {

/**
* Split join path to member to join hint and member path: `A.B.C.D.E.dim` => `[A, B, C, D, E]` + `E.dim`
* @param path
*/
public static joinHintFromPath(path: string): { path: string, joinHint: Array<string> } {
public static joinHintFromPath(path: string): { path: string, joinHint: string[] } {
const parts = path.split('.');
if (parts.length > 2) {
// Path contains join path
Expand Down Expand Up @@ -908,7 +929,7 @@ export class CubeSymbols {
}
}

protected withSymbolsCallContext(func, context) {
protected withSymbolsCallContext(func: Function, context) {
const oldContext = this.resolveSymbolsCallContext;
this.resolveSymbolsCallContext = context;
try {
Expand All @@ -933,7 +954,7 @@ export class CubeSymbols {
return this.funcArgumentsValues[funcDefinition];
}

protected joinHints() {
protected joinHints(): string | string[] | undefined {
const { joinHints } = this.resolveSymbolsCallContext || {};
if (Array.isArray(joinHints)) {
return R.uniq(joinHints);
Expand Down Expand Up @@ -1014,7 +1035,7 @@ export class CubeSymbols {
return (...filterParamArgs) => '';
}

public resolveSymbol(cubeName, name) {
public resolveSymbol(cubeName, name: string) {
const { sqlResolveFn, contextSymbols, collectJoinHints, depsResolveFn, currResolveIndexFn } = this.resolveSymbolsCallContext || {};
if (name === 'USER_CONTEXT') {
throw new Error('Support for USER_CONTEXT was removed, please migrate to SECURITY_CONTEXT.');
Expand Down
Loading
Loading