Skip to content

Commit d633e09

Browse files
committed
dev
1 parent 3368ba4 commit d633e09

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

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

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ export type CubeSymbolDefinition = {
3232
granularities?: Record<string, GranularityDefinition>;
3333
timeShift?: TimeshiftDefinition[];
3434
format?: string;
35+
drillMembers?: (...args: any[]) => ToString | ToString[];
36+
drillMemberReferences?: (...args: any[]) => ToString | ToString[];
37+
aliasMember?: string;
3538
};
3639

3740
export type HierarchyDefinition = {
@@ -638,6 +641,9 @@ export class CubeSymbols {
638641
[...memberSets.allMembers].filter(it => !memberSets.resolvedMembers.has(it)).forEach(it => {
639642
errorReporter.error(`Member '${it}' is included in '${cube.name}' but not defined in any cube`);
640643
});
644+
645+
// Add drillMembers to view measures after all include processing is complete
646+
this.validateAndAddDrillMembers(cube, errorReporter);
641647
}
642648

643649
protected applyIncludeMembers(includeMembers: any[], cube: CubeDefinition, type: string, errorReporter: ErrorReporter) {
@@ -841,6 +847,101 @@ export class CubeSymbols {
841847
});
842848
}
843849

850+
/**
851+
* Validates and adds drillMembers to view measures by checking if they're included in the view
852+
*/
853+
protected validateAndAddDrillMembers(cube: CubeDefinitionExtended, errorReporter: ErrorReporter) {
854+
if (!cube.isView || !cube.measures) {
855+
return;
856+
}
857+
858+
// Create a set of all available members in this view for quick lookup
859+
const availableViewMembers = new Set<string>();
860+
861+
// Add all dimensions and measures to the available set
862+
['dimensions', 'measures', 'segments'].forEach(type => {
863+
if (cube[type]) {
864+
Object.keys(cube[type]).forEach(memberName => {
865+
availableViewMembers.add(`${cube.name}.${memberName}`);
866+
});
867+
}
868+
});
869+
870+
// Also add included members if they exist
871+
if (cube.includedMembers) {
872+
cube.includedMembers.forEach(included => {
873+
availableViewMembers.add(included.memberPath);
874+
});
875+
}
876+
877+
// Process each measure in the view
878+
Object.entries(cube.measures).forEach(([, measureDef]) => {
879+
// Skip if this measure doesn't have an aliasMember (not from an included cube)
880+
if (!measureDef.aliasMember) {
881+
return;
882+
}
883+
884+
// Extract original cube name from aliasMember
885+
const aliasParts = measureDef.aliasMember.split('.');
886+
if (aliasParts.length < 2) {
887+
return;
888+
}
889+
890+
const originalCubeName = aliasParts[0];
891+
const originalMeasureName = aliasParts[1];
892+
893+
// Get the original measure definition from the source cube
894+
const originalMeasure = this.getResolvedMember('measures', originalCubeName, originalMeasureName);
895+
if (!originalMeasure || (!originalMeasure.drillMembers && !originalMeasure.drillMemberReferences)) {
896+
return;
897+
}
898+
899+
try {
900+
// Evaluate drillMembers at cube level like CubeToMetaTransformer does
901+
const drillMembers = originalMeasure.drillMembers || originalMeasure.drillMemberReferences;
902+
const evaluatedResult = this.evaluateReferences(
903+
originalCubeName,
904+
drillMembers,
905+
{ originalSorting: true }
906+
);
907+
908+
// Handle both string and array results from evaluateReferences
909+
let evaluatedDrillMembers: string[];
910+
if (Array.isArray(evaluatedResult)) {
911+
evaluatedDrillMembers = evaluatedResult;
912+
} else if (evaluatedResult) {
913+
evaluatedDrillMembers = [evaluatedResult];
914+
} else {
915+
evaluatedDrillMembers = [];
916+
}
917+
918+
// Filter drillMembers to only include those available in the view
919+
const validDrillMembers = evaluatedDrillMembers.filter(memberRef => availableViewMembers.has(memberRef));
920+
921+
// Only add drillMembers if we have valid ones
922+
if (validDrillMembers.length > 0) {
923+
// Add drillMembers to the measure definition - need to use any since drillMembers can be a function or array at runtime
924+
(measureDef as any).drillMembers = validDrillMembers;
925+
}
926+
927+
// Log a warning if some drillMembers were excluded
928+
if (validDrillMembers.length < evaluatedDrillMembers.length) {
929+
const excludedMembers = evaluatedDrillMembers.filter(memberRef => !availableViewMembers.has(memberRef));
930+
errorReporter.warning({
931+
message: `Some drillMembers from '${originalCubeName}.${originalMeasureName}' are not included in view '${cube.name}': ${excludedMembers.join(', ')}`,
932+
loc: null,
933+
});
934+
}
935+
} catch (error: any) {
936+
// If evaluation fails, log a warning but don't fail compilation
937+
errorReporter.warning({
938+
message: `Failed to evaluate drillMembers for '${originalCubeName}.${originalMeasureName}' in view '${cube.name}': ${error?.message || error}`,
939+
loc: null,
940+
});
941+
}
942+
});
943+
}
944+
844945
/**
845946
* This method is mainly used for evaluating RLS conditions and filters.
846947
* It allows referencing security_context (lowercase) in dynamic conditions or filter values.

0 commit comments

Comments
 (0)