@@ -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
3740export 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