Skip to content

Commit f620d89

Browse files
mcheshkovmarianore-muttdata
authored andcommitted
refactor(schema-compiler): More types for CubeEvaluator, CubeSymbols and DynamicReference (cube-js#9429)
* refactor(schema-compiler): More types in CubeEvaluator * refactor(schema-compiler): Port DynamicReference to TypeScript * refactor(schema-compiler): More types in CubeSymbols * refactor(schema-compiler): Extract ToString type
1 parent 20aa608 commit f620d89

File tree

4 files changed

+82
-31
lines changed

4 files changed

+82
-31
lines changed

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

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable no-restricted-syntax */
22
import R from 'ramda';
33

4-
import { CubeSymbols } from './CubeSymbols';
4+
import { CubeSymbols, type ToString } from './CubeSymbols';
55
import { UserError } from './UserError';
66
import { BaseQuery } from '../adapter';
77
import type { CubeValidator } from './CubeValidator';
@@ -28,7 +28,7 @@ export type DimensionDefinition = {
2828
};
2929

3030
export type TimeShiftDefinition = {
31-
timeDimension: Function,
31+
timeDimension: (...args: Array<unknown>) => ToString,
3232
interval: string,
3333
type: 'next' | 'prior',
3434
};
@@ -48,9 +48,9 @@ export type MeasureDefinition = {
4848
primaryKey?: true,
4949
drillFilters?: any,
5050
multiStage?: boolean,
51-
groupBy?: Function,
52-
reduceBy?: Function,
53-
addGroupBy?: Function,
51+
groupBy?: (...args: Array<unknown>) => Array<ToString>,
52+
reduceBy?: (...args: Array<unknown>) => Array<ToString>,
53+
addGroupBy?: (...args: Array<unknown>) => Array<ToString>,
5454
timeShift?: TimeShiftDefinition[],
5555
groupByReferences?: string[],
5656
reduceByReferences?: string[],
@@ -65,6 +65,40 @@ export type PreAggregationFilters = {
6565
scheduled?: boolean,
6666
};
6767

68+
type PreAggregationDefinition = {
69+
allowNonStrictDateRangeMatch?: boolean,
70+
timeDimensionReference?: () => ToString,
71+
granularity: string,
72+
timeDimensionReferences: Array<{ dimension: () => ToString, granularity: string }>,
73+
dimensionReferences: () => Array<ToString>,
74+
segmentReferences: () => Array<ToString>,
75+
measureReferences: () => Array<ToString>,
76+
rollupReferences: () => Array<ToString>,
77+
};
78+
79+
type PreAggregationTimeDimensionReference = {
80+
dimension: string,
81+
granularity: string,
82+
};
83+
84+
type PreAggregationReferences = {
85+
allowNonStrictDateRangeMatch?: boolean,
86+
dimensions: Array<string>,
87+
measures: Array<string>,
88+
timeDimensions: Array<PreAggregationTimeDimensionReference>,
89+
rollups: Array<string>,
90+
};
91+
92+
type PreAggregationInfo = {
93+
id: string,
94+
preAggregationName: string,
95+
preAggregation: unknown,
96+
cube: string,
97+
references: PreAggregationReferences,
98+
refreshKey: unknown,
99+
indexesReferences: unknown,
100+
};
101+
68102
export class CubeEvaluator extends CubeSymbols {
69103
public evaluatedCubes: Record<string, any> = {};
70104

@@ -492,7 +526,7 @@ export class CubeEvaluator extends CubeSymbols {
492526
/**
493527
* Returns pre-aggregations filtered by the specified selector.
494528
*/
495-
public preAggregations(filter: PreAggregationFilters) {
529+
public preAggregations(filter: PreAggregationFilters): Array<PreAggregationInfo> {
496530
const { scheduled, dataSources, cubes, preAggregationIds } = filter || {};
497531
const idFactory = ({ cube, preAggregationName }) => `${cube}.${preAggregationName}`;
498532

@@ -510,7 +544,7 @@ export class CubeEvaluator extends CubeSymbols {
510544
)
511545
)
512546
))
513-
.map(cube => {
547+
.flatMap(cube => {
514548
const preAggregations = this.preAggregationsForCube(cube);
515549
return Object.keys(preAggregations)
516550
.filter(
@@ -547,11 +581,10 @@ export class CubeEvaluator extends CubeSymbols {
547581
}, {})
548582
};
549583
});
550-
})
551-
.reduce((a, b) => a.concat(b), []);
584+
});
552585
}
553586

554-
public scheduledPreAggregations() {
587+
public scheduledPreAggregations(): Array<PreAggregationInfo> {
555588
return this.preAggregations({ scheduled: true });
556589
}
557590

@@ -704,8 +737,8 @@ export class CubeEvaluator extends CubeSymbols {
704737
return { cubeReferencesUsed, pathReferencesUsed, evaluatedSql };
705738
}
706739

707-
protected evaluatePreAggregationReferences(cube, aggregation) {
708-
const timeDimensions: any = [];
740+
protected evaluatePreAggregationReferences(cube: string, aggregation: PreAggregationDefinition): PreAggregationReferences {
741+
const timeDimensions: Array<PreAggregationTimeDimensionReference> = [];
709742

710743
if (aggregation.timeDimensionReference) {
711744
timeDimensions.push({

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

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import { BaseQuery } from '../adapter';
99

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

12+
export type ToString = { toString(): string };
13+
1214
interface CubeDefinition {
1315
name: string;
14-
extends?: string;
16+
extends?: (...args: Array<unknown>) => { __cubeName: string };
1517
measures?: Record<string, any>;
1618
dimensions?: Record<string, any>;
1719
segments?: Record<string, any>;
@@ -292,7 +294,8 @@ export class CubeSymbols {
292294
// If the hierarchy is included all members from it should be included as well
293295
// Extend `includes` with members from hierarchies that should be auto-included
294296
const cubes = type === 'dimensions' ? cube.cubes.map((it) => {
295-
const fullPath = this.evaluateReferences(null, it.joinPath, { collectJoinHints: true });
297+
// TODO recheck `it.joinPath` typing
298+
const fullPath = this.evaluateReferences(null, it.joinPath as () => ToString, { collectJoinHints: true });
296299
const split = fullPath.split('.');
297300
const cubeRef = split[split.length - 1];
298301

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

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

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

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

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

553-
protected evaluateReferences(cube, referencesFn, options: any = {}) {
558+
protected evaluateReferences<T extends ToString | Array<ToString>>(
559+
cube: string | null,
560+
referencesFn: (...args: Array<unknown>) => T,
561+
options: { collectJoinHints?: boolean, originalSorting?: boolean } = {}
562+
):
563+
T extends Array<ToString> ? Array<string> : T extends ToString ? string : string | Array<string> {
554564
const cubeEvaluator = this;
555565

556566
const fullPath = (joinHints, path) => {
@@ -561,7 +571,7 @@ export class CubeSymbols {
561571
}
562572
};
563573

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

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

592608
public pathFromArray(array) {
593609
return array.join('.');
594610
}
595611

596-
protected resolveSymbolsCall(func, nameResolver, context?: any) {
612+
protected resolveSymbolsCall<T>(
613+
func: (...args: Array<unknown>) => T | DynamicReference<T>,
614+
nameResolver: (id: string) => unknown,
615+
context?: unknown,
616+
): T {
597617
const oldContext = this.resolveSymbolsCallContext;
598618
this.resolveSymbolsCallContext = context;
599619
try {
600620
// eslint-disable-next-line prefer-spread
601-
let res = func.apply(null, this.funcArguments(func).map((id) => nameResolver(id.trim())));
621+
const res = func.apply(null, this.funcArguments(func).map((id) => nameResolver(id.trim())));
602622
if (res instanceof DynamicReference) {
603-
res = res.fn.apply(null, res.memberNames.map((id) => nameResolver(id.trim())));
623+
return res.fn.apply(null, res.memberNames.map((id) => nameResolver(id.trim())));
604624
}
605625
return res;
606626
} finally {

packages/cubejs-schema-compiler/src/compiler/DynamicReference.js

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export class DynamicReference<T> {
2+
public constructor(public memberNames: Array<string>, public fn: (...args: Array<unknown>) => T) {
3+
}
4+
}

0 commit comments

Comments
 (0)