Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
57 changes: 45 additions & 12 deletions packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable no-restricted-syntax */
import R from 'ramda';

import { CubeSymbols } from './CubeSymbols';
import { CubeSymbols, type ToString } from './CubeSymbols';
import { UserError } from './UserError';
import { BaseQuery } from '../adapter';
import type { CubeValidator } from './CubeValidator';
Expand All @@ -28,7 +28,7 @@
};

export type TimeShiftDefinition = {
timeDimension: Function,
timeDimension: (...args: Array<unknown>) => ToString,
interval: string,
type: 'next' | 'prior',
};
Expand All @@ -48,9 +48,9 @@
primaryKey?: true,
drillFilters?: any,
multiStage?: boolean,
groupBy?: Function,
reduceBy?: Function,
addGroupBy?: Function,
groupBy?: (...args: Array<unknown>) => Array<ToString>,
reduceBy?: (...args: Array<unknown>) => Array<ToString>,
addGroupBy?: (...args: Array<unknown>) => Array<ToString>,
timeShift?: TimeShiftDefinition[],
groupByReferences?: string[],
reduceByReferences?: string[],
Expand All @@ -65,6 +65,40 @@
scheduled?: boolean,
};

type PreAggregationDefinition = {
allowNonStrictDateRangeMatch?: boolean,
timeDimensionReference?: () => ToString,
granularity: string,
timeDimensionReferences: Array<{ dimension: () => ToString, granularity: string }>,
dimensionReferences: () => Array<ToString>,
segmentReferences: () => Array<ToString>,
measureReferences: () => Array<ToString>,
rollupReferences: () => Array<ToString>,
};

type PreAggregationTimeDimensionReference = {
dimension: string,
granularity: string,
};

type PreAggregationReferences = {
allowNonStrictDateRangeMatch?: boolean,
dimensions: Array<string>,
measures: Array<string>,
timeDimensions: Array<PreAggregationTimeDimensionReference>,
rollups: Array<string>,
};

type PreAggregationInfo = {
id: string,
preAggregationName: string,
preAggregation: unknown,
cube: string,
references: PreAggregationReferences,
refreshKey: unknown,
indexesReferences: unknown,
};

export class CubeEvaluator extends CubeSymbols {
public evaluatedCubes: Record<string, any> = {};

Expand Down Expand Up @@ -492,7 +526,7 @@
/**
* Returns pre-aggregations filtered by the specified selector.
*/
public preAggregations(filter: PreAggregationFilters) {
public preAggregations(filter: PreAggregationFilters): Array<PreAggregationInfo> {

Check warning on line 529 in packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts

View check run for this annotation

Codecov / codecov/patch

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

Added line #L529 was not covered by tests
const { scheduled, dataSources, cubes, preAggregationIds } = filter || {};
const idFactory = ({ cube, preAggregationName }) => `${cube}.${preAggregationName}`;

Expand All @@ -510,7 +544,7 @@
)
)
))
.map(cube => {
.flatMap(cube => {

Check warning on line 547 in packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts

View check run for this annotation

Codecov / codecov/patch

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

Added line #L547 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻

const preAggregations = this.preAggregationsForCube(cube);
return Object.keys(preAggregations)
.filter(
Expand Down Expand Up @@ -547,11 +581,10 @@
}, {})
};
});
})
.reduce((a, b) => a.concat(b), []);
});
}

public scheduledPreAggregations() {
public scheduledPreAggregations(): Array<PreAggregationInfo> {

Check warning on line 587 in packages/cubejs-schema-compiler/src/compiler/CubeEvaluator.ts

View check run for this annotation

Codecov / codecov/patch

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

Added line #L587 was not covered by tests
return this.preAggregations({ scheduled: true });
}

Expand Down Expand Up @@ -704,8 +737,8 @@
return { cubeReferencesUsed, pathReferencesUsed, evaluatedSql };
}

protected evaluatePreAggregationReferences(cube, aggregation) {
const timeDimensions: any = [];
protected evaluatePreAggregationReferences(cube: string, aggregation: PreAggregationDefinition): PreAggregationReferences {
const timeDimensions: Array<PreAggregationTimeDimensionReference> = [];

if (aggregation.timeDimensionReference) {
timeDimensions.push({
Expand Down
46 changes: 33 additions & 13 deletions packages/cubejs-schema-compiler/src/compiler/CubeSymbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { BaseQuery } from '../adapter';

import type { ErrorReporter } from './ErrorReporter';

export type ToString = { toString(): string };

interface CubeDefinition {
name: string;
extends?: string;
extends?: (...args: Array<unknown>) => { __cubeName: string };
measures?: Record<string, any>;
dimensions?: Record<string, any>;
segments?: Record<string, any>;
Expand Down Expand Up @@ -292,7 +294,8 @@ export class CubeSymbols {
// If the hierarchy is included all members from it should be included as well
// Extend `includes` with members from hierarchies that should be auto-included
const cubes = type === 'dimensions' ? cube.cubes.map((it) => {
const fullPath = this.evaluateReferences(null, it.joinPath, { collectJoinHints: true });
// TODO recheck `it.joinPath` typing
const fullPath = this.evaluateReferences(null, it.joinPath as () => ToString, { collectJoinHints: true });
const split = fullPath.split('.');
const cubeRef = split[split.length - 1];

Expand Down Expand Up @@ -332,7 +335,8 @@ export class CubeSymbols {
const hierarchy = this.getResolvedMember(type, cubeName, hierarchyName);

if (hierarchy) {
const levels = this.evaluateReferences(cubeName, this.getResolvedMember('hierarchies', cubeName, hierarchyName).levels, { originalSorting: true });
// TODO recheck `this.getResolvedMember(...).levels` typing
const levels = this.evaluateReferences(cubeName, this.getResolvedMember('hierarchies', cubeName, hierarchyName).levels as () => Array<ToString>, { originalSorting: true });

levels.forEach((level) => autoIncludeMembers.add(level));
}
Expand Down Expand Up @@ -370,7 +374,8 @@ export class CubeSymbols {

protected membersFromCubes(parentCube: CubeDefinition, cubes: any[], type: string, errorReporter: ErrorReporter, splitViews: SplitViews, memberSets: any) {
return R.unnest(cubes.map(cubeInclude => {
const fullPath = this.evaluateReferences(null, cubeInclude.joinPath, { collectJoinHints: true });
// TODO recheck `cubeInclude.joinPath` typing
const fullPath = this.evaluateReferences(null, cubeInclude.joinPath as () => ToString, { collectJoinHints: true });
const split = fullPath.split('.');
const cubeReference = split[split.length - 1];
const cubeName = cubeInclude.alias || cubeReference;
Expand Down Expand Up @@ -453,7 +458,7 @@ export class CubeSymbols {
return includes.filter(include => !excludesMap.has(include.member));
}

protected membersFromIncludeExclude(referencesFn: any, cubeName: string, type: string) {
protected membersFromIncludeExclude(referencesFn: (...args: Array<unknown>) => Array<ToString>, cubeName: string, type: string) {
const references = this.evaluateReferences(cubeName, referencesFn);
return R.unnest(references.map((ref: string) => {
const path = ref.split('.');
Expand Down Expand Up @@ -550,7 +555,12 @@ export class CubeSymbols {
return res;
}

protected evaluateReferences(cube, referencesFn, options: any = {}) {
protected evaluateReferences<T extends ToString | Array<ToString>>(
cube: string | null,
referencesFn: (...args: Array<unknown>) => T,
options: { collectJoinHints?: boolean, originalSorting?: boolean } = {}
):
T extends Array<ToString> ? Array<string> : T extends ToString ? string : string | Array<string> {
const cubeEvaluator = this;

const fullPath = (joinHints, path) => {
Expand All @@ -561,7 +571,7 @@ export class CubeSymbols {
}
};

const arrayOrSingle = cubeEvaluator.resolveSymbolsCall(referencesFn, (name) => {
const arrayOrSingle: T = cubeEvaluator.resolveSymbolsCall(referencesFn, (name) => {
const referencedCube = cubeEvaluator.symbols[name] && name || cube;
const resolvedSymbol =
cubeEvaluator.resolveSymbol(
Expand All @@ -582,25 +592,35 @@ export class CubeSymbols {
collectJoinHints: options.collectJoinHints,
});
if (!Array.isArray(arrayOrSingle)) {
return arrayOrSingle.toString();
// arrayOrSingle is of type `T`, and we just checked that it is not an array
// Which means it `T` be an object with `toString`, and result must be `string`
// For any branch of return type that can can contain just an object it's OK to return string
return arrayOrSingle.toString() as any;
}

const references = arrayOrSingle.map(p => p.toString());
return options.originalSorting ? references : R.sortBy(R.identity, references);
const references: Array<string> = arrayOrSingle.map(p => p.toString());
// arrayOrSingle is of type `T`, and we just checked that it is an array
// Which means that both `T` and result must be arrays
// For any branch of return type that can contain array it's OK to return array
return options.originalSorting ? references : R.sortBy(R.identity, references) as any;
}

public pathFromArray(array) {
return array.join('.');
}

protected resolveSymbolsCall(func, nameResolver, context?: any) {
protected resolveSymbolsCall<T>(
func: (...args: Array<unknown>) => T | DynamicReference<T>,
nameResolver: (id: string) => unknown,
context?: unknown,
): T {
const oldContext = this.resolveSymbolsCallContext;
this.resolveSymbolsCallContext = context;
try {
// eslint-disable-next-line prefer-spread
let res = func.apply(null, this.funcArguments(func).map((id) => nameResolver(id.trim())));
const res = func.apply(null, this.funcArguments(func).map((id) => nameResolver(id.trim())));
if (res instanceof DynamicReference) {
res = res.fn.apply(null, res.memberNames.map((id) => nameResolver(id.trim())));
return res.fn.apply(null, res.memberNames.map((id) => nameResolver(id.trim())));
}
return res;
} finally {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class DynamicReference<T> {
public constructor(public memberNames: Array<string>, public fn: (...args: Array<unknown>) => T) {
}
}
Loading