diff --git a/services/api/src/acquisition/acquisition.service.ts b/services/api/src/acquisition/acquisition.service.ts index 25d1683..6938712 100644 --- a/services/api/src/acquisition/acquisition.service.ts +++ b/services/api/src/acquisition/acquisition.service.ts @@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectDataSource } from '@nestjs/typeorm'; import { DataSource } from 'typeorm'; import { AcquisitionQueryDto, AcquisitionCompareDto, ReferrerQueryDto } from './dto/acquisition-query.dto'; +import { resolveCorpId, corpSessionFilter, corpRawEventsFilter } from '../common/corp-filter.util'; export interface ChannelMetrics { channel: string; @@ -81,6 +82,9 @@ export class AcquisitionService { */ async getOverview(query: AcquisitionQueryDto): Promise { const { startDate, endDate, limit } = query; + const corpId = await resolveCorpId(this.dataSource, query.corp); + const overviewCorpClause = corpSessionFilter(3, corpId); + const channelsCorpClause = corpSessionFilter(4, corpId); const overviewQuery = ` WITH session_data AS ( @@ -104,7 +108,7 @@ export class AcquisitionService { ), 0) as page_views FROM session_fingerprints sf WHERE sf."createdAt" BETWEEN $1 AND $2 - AND sf."isBot" = false + AND sf."isBot" = false${overviewCorpClause} ), user_first AS ( SELECT "userId", MIN("createdAt") as first_session @@ -147,7 +151,7 @@ export class AcquisitionService { ), 0) as revenue FROM session_fingerprints sf WHERE sf."createdAt" BETWEEN $1 AND $2 - AND sf."isBot" = false + AND sf."isBot" = false${channelsCorpClause} ), user_first AS ( SELECT "userId", MIN("createdAt") as first_session @@ -170,10 +174,15 @@ export class AcquisitionService { LIMIT $3 `; + const overviewParams: (string | number)[] = [startDate, endDate]; + if (corpId !== null) overviewParams.push(corpId); + const channelsParams: (string | number)[] = [startDate, endDate, limit ?? 20]; + if (corpId !== null) channelsParams.push(corpId); + try { const [overviewResult, channelsResult] = await Promise.all([ - this.dataSource.query(overviewQuery, [startDate, endDate]), - this.dataSource.query(channelsQuery, [startDate, endDate, limit ?? 20]), + this.dataSource.query(overviewQuery, overviewParams), + this.dataSource.query(channelsQuery, channelsParams), ]); const overview = overviewResult[0] || {}; @@ -229,8 +238,11 @@ export class AcquisitionService { */ async getSources(query: AcquisitionQueryDto): Promise { const { startDate, endDate, limit } = query; + const corpId = await resolveCorpId(this.dataSource, query.corp); // First try the aggregated_metrics traffic_source dimension (populated by processor) + // NOTE: aggregated_metrics has no corp dimension — when a corp scope is requested, + // skip this path and go straight to raw_events derivation (which IS corp-scoped). const aggregatedQuery = ` WITH source_totals AS ( SELECT @@ -249,7 +261,9 @@ export class AcquisitionService { `; try { - const aggregated = await this.dataSource.query(aggregatedQuery, [startDate, endDate, limit ?? 20]); + const aggregated = corpId === null + ? await this.dataSource.query(aggregatedQuery, [startDate, endDate, limit ?? 20]) + : []; if (aggregated.length > 0) { return aggregated.map((row: Record) => ({ @@ -286,7 +300,7 @@ export class AcquisitionService { FROM raw_events WHERE "eventType" IN ('pageview', 'pageView') AND "deviceType" IS DISTINCT FROM 'bot' - AND timestamp BETWEEN $1 AND $2 + AND timestamp BETWEEN $1 AND $2${corpRawEventsFilter(4, corpId, 'raw_events')} ORDER BY "sessionId", timestamp ASC ) SELECT @@ -301,7 +315,9 @@ export class AcquisitionService { LIMIT $3 `; - const raw = await this.dataSource.query(rawQuery, [startDate, endDate, limit ?? 20]); + const rawParams: (string | number)[] = [startDate, endDate, limit ?? 20]; + if (corpId !== null) rawParams.push(corpId); + const raw = await this.dataSource.query(rawQuery, rawParams); return raw.map((row: Record) => ({ source: row.source as string, @@ -323,6 +339,7 @@ export class AcquisitionService { */ async getCampaigns(query: AcquisitionQueryDto): Promise { const { startDate, endDate, source, limit } = query; + const corpId = await resolveCorpId(this.dataSource, query.corp); const conditions: string[] = [ 'sf."createdAt" BETWEEN $1 AND $2', @@ -338,6 +355,13 @@ export class AcquisitionService { paramIndex++; } + if (corpId !== null) { + // Strip leading ' AND ' so the fragment slots into the AND-joined conditions list. + conditions.push(corpSessionFilter(paramIndex, corpId).replace(/^\s*AND\s+/i, '')); + params.push(corpId); + paramIndex++; + } + const whereClause = conditions.join(' AND '); const campaignsQuery = ` @@ -415,6 +439,7 @@ export class AcquisitionService { */ async getReferrers(query: ReferrerQueryDto): Promise { const { startDate, endDate, excludeInternal, limit } = query; + const corpId = await resolveCorpId(this.dataSource, query.corp); const conditions: string[] = [ 'sf."createdAt" BETWEEN $1 AND $2', @@ -422,12 +447,19 @@ export class AcquisitionService { 'sf.referrer IS NOT NULL', ]; const params: (string | number)[] = [startDate, endDate]; + let paramIndex = 3; if (excludeInternal) { conditions.push("sf.referrer NOT LIKE '%atlilith.%'"); conditions.push("sf.referrer NOT LIKE '%trustedmeet.%'"); } + if (corpId !== null) { + conditions.push(corpSessionFilter(paramIndex, corpId).replace(/^\s*AND\s+/i, '')); + params.push(corpId); + paramIndex++; + } + const whereClause = conditions.join(' AND '); const referrersQuery = ` @@ -464,7 +496,7 @@ export class AcquisitionService { WHERE sd.domain IS NOT NULL GROUP BY sd.referrer, sd.domain ORDER BY sessions DESC - LIMIT $3 + LIMIT $${paramIndex} `; params.push(limit ?? 20); @@ -499,12 +531,14 @@ export class AcquisitionService { startDate: query.startDate, endDate: query.endDate, limit: query.limit, + corp: query.corp, }); const previous = await this.getOverview({ startDate: query.compareStartDate, endDate: query.compareEndDate, limit: query.limit, + corp: query.corp, }); const calcChange = (curr: number, prev: number) => diff --git a/services/api/src/acquisition/dto/acquisition-query.dto.ts b/services/api/src/acquisition/dto/acquisition-query.dto.ts index 4f2bbee..4067e62 100644 --- a/services/api/src/acquisition/dto/acquisition-query.dto.ts +++ b/services/api/src/acquisition/dto/acquisition-query.dto.ts @@ -8,6 +8,10 @@ export class AcquisitionQueryDto { @IsDateString() endDate!: string; + @IsOptional() + @IsString() + corp?: string; + @IsOptional() @IsString() channel?: string; @@ -55,6 +59,10 @@ export class ReferrerQueryDto { @IsDateString() endDate!: string; + @IsOptional() + @IsString() + corp?: string; + @IsOptional() @Transform(({ value }) => value === 'true' || value === true) @IsBoolean()