Skip to content

Commit d3bd0f2

Browse files
Refactor UUID handling and improve metrics data sanitization
- Updated the `bulkId` generation in `seedDummyEmails` for better idempotency. - Introduced `normalizeUuidFromEvent` function to sanitize user and team IDs in metrics loading functions, ensuring only valid UUIDs are processed. - Enhanced the logic in `loadDailyActiveUsersSplit` and `loadDailyActiveTeamsSplit` to utilize sanitized data, improving data integrity and accuracy in metrics retrieval. - Removed unnecessary mouse leave event handlers in `HeroAnalyticsWidget` for cleaner interaction.
1 parent d15f3f9 commit d3bd0f2

File tree

3 files changed

+27
-8
lines changed

3 files changed

+27
-8
lines changed

apps/backend/prisma/seed.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2112,7 +2112,7 @@ async function seedDummyEmails(options: EmailSeedOptions) {
21122112

21132113
for (let j = 0; j < count; j++) {
21142114
// Deterministic ID so seeding is idempotent across runs
2115-
const bulkId = `66000000-bulk-seed-${String(emailBulkIndex).padStart(4, '0')}-000000000000`;
2115+
const bulkId = `66000000-0000-4000-8000-${emailBulkIndex.toString(16).padStart(12, '0')}`;
21162116
const hour = 7 + Math.floor(emailBulkRand() * 14);
21172117
const createdAt = emailDaysAgo(dayBack, hour);
21182118
const subject = bulkEmailSubjects[Math.floor(emailBulkRand() * bulkEmailSubjects.length)];

apps/backend/src/app/api/latest/internal/metrics/route.tsx

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { KnownErrors } from "@stackframe/stack-shared";
66
import { UsersCrud } from "@stackframe/stack-shared/dist/interface/crud/users";
77
import { getNodeEnvironment } from "@stackframe/stack-shared/dist/utils/env";
88
import { captureError, StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
9+
import { isUuid } from "@stackframe/stack-shared/dist/utils/uuids";
910
import { adaptSchema, adminAuthTypeSchema, yupArray, yupMixed, yupNumber, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
1011
import yup from 'yup';
1112
import { userFullInclude, userPrismaToCrud, usersCrudHandlers } from "../../users/crud";
@@ -24,6 +25,11 @@ function formatClickhouseDateTimeParam(date: Date): string {
2425
return date.toISOString().slice(0, 19);
2526
}
2627

28+
function normalizeUuidFromEvent(value: string): string | null {
29+
const normalized = value.trim().toLowerCase();
30+
return isUuid(normalized) ? normalized : null;
31+
}
32+
2733
async function loadUsersByCountry(tenancy: Tenancy, includeAnonymous: boolean = false): Promise<Record<string, number>> {
2834
const clickhouseClient = getClickhouseAdminClient();
2935
const res = await clickhouseClient.query({
@@ -252,7 +258,15 @@ async function loadDailyActiveUsersSplit(tenancy: Tenancy, now: Date, includeAno
252258
format: "JSONEachRow",
253259
}).then((result) => result.json() as Promise<{ day: string, user_id: string }[]>);
254260

255-
const activeUserIds = [...new Set(userRows.map((row) => row.user_id))];
261+
const sanitizedUserRows = userRows.flatMap((row) => {
262+
const userId = normalizeUuidFromEvent(row.user_id);
263+
if (userId == null) {
264+
return [];
265+
}
266+
return [{ ...row, user_id: userId }];
267+
});
268+
269+
const activeUserIds = [...new Set(sanitizedUserRows.map((row) => row.user_id))];
256270
const users = activeUserIds.length === 0
257271
? []
258272
: await prisma.projectUser.findMany({
@@ -274,7 +288,7 @@ async function loadDailyActiveUsersSplit(tenancy: Tenancy, now: Date, includeAno
274288
orderedDays.push(date);
275289
idsByDay.set(date, new Set<string>());
276290
}
277-
for (const row of userRows) {
291+
for (const row of sanitizedUserRows) {
278292
const day = row.day.split('T')[0];
279293
const daySet = idsByDay.get(day);
280294
if (daySet) {
@@ -324,7 +338,15 @@ async function loadDailyActiveTeamsSplit(tenancy: Tenancy, now: Date): Promise<A
324338
format: "JSONEachRow",
325339
}).then((result) => result.json() as Promise<{ day: string, team_id: string }[]>);
326340

327-
const activeTeamIds = [...new Set(teamRows.map((row) => row.team_id))];
341+
const sanitizedTeamRows = teamRows.flatMap((row) => {
342+
const teamId = normalizeUuidFromEvent(row.team_id);
343+
if (teamId == null) {
344+
return [];
345+
}
346+
return [{ ...row, team_id: teamId }];
347+
});
348+
349+
const activeTeamIds = [...new Set(sanitizedTeamRows.map((row) => row.team_id))];
328350
const teams = activeTeamIds.length === 0
329351
? []
330352
: await prisma.team.findMany({
@@ -345,7 +367,7 @@ async function loadDailyActiveTeamsSplit(tenancy: Tenancy, now: Date): Promise<A
345367
orderedDays.push(date);
346368
idsByDay.set(date, new Set<string>());
347369
}
348-
for (const row of teamRows) {
370+
for (const row of sanitizedTeamRows) {
349371
const day = row.day.split('T')[0];
350372
const daySet = idsByDay.get(day);
351373
if (daySet) {

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/metrics-page.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,6 @@ function HeroAnalyticsWidget({
378378
color={dauColor}
379379
isHovered={chartMode === 'dau'}
380380
onMouseEnter={() => handlePillMouseEnter('dau')}
381-
onMouseLeave={handlePillMouseLeave}
382381
/>
383382
<div className="w-px bg-foreground/[0.07] shrink-0 my-1.5 mx-1" />
384383
<HeroInChartPill
@@ -388,7 +387,6 @@ function HeroAnalyticsWidget({
388387
color={visitorsColor}
389388
isHovered={chartMode === 'visitors'}
390389
onMouseEnter={() => handlePillMouseEnter('visitors')}
391-
onMouseLeave={handlePillMouseLeave}
392390
/>
393391
<div className="w-px bg-foreground/[0.07] shrink-0 my-1.5 mx-1" />
394392
<HeroInChartPill
@@ -398,7 +396,6 @@ function HeroAnalyticsWidget({
398396
color={revenueColor}
399397
isHovered={chartMode === 'revenue'}
400398
onMouseEnter={() => handlePillMouseEnter('revenue')}
401-
onMouseLeave={handlePillMouseLeave}
402399
/>
403400
</div>
404401

0 commit comments

Comments
 (0)