Skip to content

Commit

Permalink
add tracking error and query type behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
invisal committed Jan 9, 2025
1 parent 9c7b74f commit feee98a
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 66 deletions.
20 changes: 0 additions & 20 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,6 @@ const nextConfig = {
env: {
NEXT_PUBLIC_STUDIO_VERSION: pkg.version,
},
headers: async () => {
return [
{
source: "/api/events",
headers: [
{ key: "Access-Control-Allow-Origin", value: "*" },
{ key: "Access-Control-Allow-Credentials", value: "true" },
{
key: "Access-Control-Allow-Headers",
value:
"X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date",
},
{
key: "Access-Control-Allow-Methods",
value: "GET,DELETE,PATCH,POST,PUT",
},
],
},
];
},
};

module.exports = withMDX(nextConfig);
33 changes: 23 additions & 10 deletions src/app/api/events/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
//
// All recorded data will be stored in the Starbase Database.

import { cookies } from "next/headers";
import { headers } from "next/headers";
import zod from "zod";
import { NextRequest, NextResponse } from "next/server";
import { after, NextRequest, NextResponse } from "next/server";
import { insertTrackingRecord } from "@/lib/api/insert-tracking-record";

const eventBodySchema = zod.object({
Expand All @@ -20,17 +20,26 @@ const eventBodySchema = zod.object({
.min(1),
});

export async function OPTIONS() {
// Handle preflight requests
return new NextResponse(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization, x-ob-id",
},
});
}

export const POST = async (req: NextRequest) => {
// Getting the device id
const cookieStore = await cookies();

let deviceId = cookieStore.get("od-id")?.value;
const headerStore = await headers();
const deviceId = headerStore.get("x-od-id");

if (!deviceId) {
deviceId = crypto.randomUUID();

cookieStore.set("od-id", deviceId, {
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
return NextResponse.json({
success: false,
error: "Device ID is required",
});
}

Expand All @@ -46,7 +55,11 @@ export const POST = async (req: NextRequest) => {
}

// Save the event
await insertTrackingRecord(deviceId, validate.data.events.slice(0, 50));
after(() => {
insertTrackingRecord(deviceId, validate.data.events.slice(0, 50))
.then()
.catch();
});

return NextResponse.json({
success: true,
Expand Down
27 changes: 25 additions & 2 deletions src/components/gui/database-gui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
ResizablePanel,
ResizablePanelGroup,
} from "@/components/ui/resizable";
import { useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { openTab } from "@/messages/open-tab";
import WindowTabs, { WindowTabItemProps } from "./windows-tab";
import useMessageListener from "@/components/hooks/useMessageListener";
Expand All @@ -21,6 +21,7 @@ import { useSchema } from "@/context/schema-provider";
import { Binoculars, GearSix, Table } from "@phosphor-icons/react";
import DoltSidebar from "./database-specified/dolt/dolt-sidebar";
import { DoltIcon } from "../icons/outerbase-icon";
import { normalizedPathname, sendAnalyticEvents } from "@/lib/tracking";

export default function DatabaseGui() {
const DEFAULT_WIDTH = 300;
Expand All @@ -43,6 +44,7 @@ export default function DatabaseGui() {
key: "query",
component: <QueryWindow initialName="Query" />,
icon: Binoculars,
type: "query",
},
]);

Expand Down Expand Up @@ -137,6 +139,27 @@ export default function DatabaseGui() {
].filter(Boolean) as { text: string; onClick: () => void }[];
}, [currentSchemaName, databaseDriver]);

const onTabSelectChange = useCallback(
(newTabIndex: number) => {
setSelectedTabIndex(newTabIndex);

const currentTab = tabs[newTabIndex];
if (currentTab) {
sendAnalyticEvents([
{
name: "page_view",
data: {
path: normalizedPathname(window.location.pathname),
tab: currentTab.type,
tab_key: currentTab.key,
},
},
]);
}
},
[tabs]
);

return (
<div className="h-screen w-screen flex flex-col">
<ResizablePanelGroup direction="horizontal">
Expand All @@ -149,7 +172,7 @@ export default function DatabaseGui() {
menu={tabSideMenu}
tabs={tabs}
selected={selectedTabIndex}
onSelectChange={setSelectedTabIndex}
onSelectChange={onTabSelectChange}
onTabsChange={setTabs}
/>
</ResizablePanel>
Expand Down
21 changes: 11 additions & 10 deletions src/components/gui/windows-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface WindowTabItemProps {
title: string;
identifier: string;
key: string;
type?: string;
}

interface WindowTabsProps {
Expand Down Expand Up @@ -167,18 +168,18 @@ export default function WindowTabs({
hideCloseButton
? undefined
: () => {
const newTabs = tabs.filter(
(t) => t.key !== tab.key
);
const newTabs = tabs.filter(
(t) => t.key !== tab.key
);

if (selected >= idx) {
onSelectChange(newTabs.length - 1);
}
if (selected >= idx) {
onSelectChange(newTabs.length - 1);
}

if (onTabsChange) {
onTabsChange(newTabs);
if (onTabsChange) {
onTabsChange(newTabs);
}
}
}
}
/>
))}
Expand Down Expand Up @@ -220,7 +221,7 @@ export default function WindowTabs({
<div
className="absolute left-0 right-0 top-0 bottom-0"
style={{
display: tabIndex === selected ? 'inherit' : 'none'
display: tabIndex === selected ? "inherit" : "none",
}}
>
{tab.component}
Expand Down
26 changes: 26 additions & 0 deletions src/components/lib/multiple-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import {
DatabaseResultSet,
DatabaseResultStat,
} from "@/drivers/base-driver";
import { getSQLStatementType, SQLStatementType } from "@/drivers/sql-helper";
import { sendAnalyticEvents } from "@/lib/tracking";

export interface MultipleQueryProgressItem {
order: number;
sql: string;
statementType?: SQLStatementType;
start: number;
end?: number;
stats?: DatabaseResultStat;
Expand Down Expand Up @@ -39,14 +42,17 @@ export async function multipleQuery(

for (let i = 0; i < statements.length; i++) {
const statement = statements[i] as string;
const statementType = statement ? getSQLStatementType(statement) : "OTHER";

const log: MultipleQueryProgressItem = {
order: i,
sql: statement,
start: Date.now(),
statementType,
};

logs.push(log);

if (onProgress) {
onProgress({ logs, progress: i, total: statements.length });
}
Expand Down Expand Up @@ -85,5 +91,25 @@ export async function multipleQuery(
}
}

sendAnalyticEvents([
...logs.map((entry) => {
return {
name: "evt_query_execute",
data: {
sql: entry.statementType ?? "OTHER",
type: entry.statementType ?? "OTHER",
},
};
}),
...result.map((entry) => {
return {
name: "evt_rows_queried",
data: {
count: entry.result.rows.length,
},
};
}),
]);

return { result, logs };
}
81 changes: 64 additions & 17 deletions src/components/page-tracker.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { addTrackEvent, normalizedPathname } from "@/lib/tracking";
import { sendAnalyticEvents, normalizedPathname } from "@/lib/tracking";
import { usePathname } from "next/navigation";
import { useEffect } from "react";

Expand All @@ -11,33 +11,80 @@ export default function PageTracker() {
useEffect(() => {
const normalized = normalizedPathname(pathname);

addTrackEvent("pageview", {
path: normalized,
full_path: pathname === normalized ? undefined : pathname,
});
sendAnalyticEvents([
{
name: "page_view",
data: {
path: normalized,
full_path: pathname === normalized ? undefined : pathname,
},
},
]);
}, [pathname]);

// Track unhandle rejection
useEffect(() => {
window.addEventListener("unhandledrejection", (event) => {
if (typeof window === "undefined") return;

const handler = (event: PromiseRejectionEvent) => {
if (typeof event.reason === "string") {
addTrackEvent("unhandledrejection", {
message: event.reason,
});
sendAnalyticEvents([
{
name: "unhandledrejection",
data: { message: event.reason, path: window.location.pathname },
},
]);
} else if (event.reason?.message) {
addTrackEvent("unhandledrejection", {
message: event.reason.message,
stack: event.reason.stack,
});
sendAnalyticEvents([
{
name: "unhandledrejection",
data: {
message: event.reason.message,
stack: event.reason.stack,
path: window.location.pathname,
},
},
]);
} else {
addTrackEvent("unhandledrejection", event.toString());
sendAnalyticEvents([
{
name: "unhandledrejection",
data: {
message: event.toString(),
},
},
]);
}
});
};

window.addEventListener("unhandledrejection", handler);

return () => {
window.removeEventListener("unhandledrejection", () => {});
window.removeEventListener("unhandledrejection", handler);
};
}, []);

return null;
// Track other unhandled error
useEffect(() => {
if (typeof window === "undefined") return;

const handler = (event: ErrorEvent) => {
sendAnalyticEvents([
{
name: "error",
data: {
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
},
},
]);
};

window.addEventListener("error", handler);
return () => {
window.removeEventListener("error", handler);
};
}, []);
}
39 changes: 39 additions & 0 deletions src/drivers/sql-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export type SQLStatementType =
| "SELECT"
| "INSERT"
| "UPDATE"
| "CREATE_TABLE"
| "ALTER_TABLE"
| "DROP_TABLE"
| "CREATE_INDEX"
| "DROP_INDEX"
| "CREATE_VIEW"
| "DROP_VIEW"
| "CREATE_TRIGGER"
| "DROP_TRIGGER"
| "OTHER";

export function getSQLStatementType(statement: string): SQLStatementType {
let trimmed = statement.trim().toUpperCase();

// Reduce continuous whitespaces to single whitespace
trimmed = trimmed.replace(/\s+/g, " ");

// Replace the "IF NOT EXISTS" clause with an empty string
trimmed = trimmed.replace("IF NOT EXISTS", "");

if (trimmed.startsWith("SELECT")) return "SELECT";
if (trimmed.startsWith("INSERT")) return "INSERT";
if (trimmed.startsWith("UPDATE")) return "UPDATE";
if (trimmed.startsWith("CREATE TABLE")) return "CREATE_TABLE";
if (trimmed.startsWith("ALTER TABLE")) return "ALTER_TABLE";
if (trimmed.startsWith("DROP TABLE")) return "DROP_TABLE";
if (trimmed.startsWith("CREATE INDEX")) return "CREATE_INDEX";
if (trimmed.startsWith("DROP INDEX")) return "DROP_INDEX";
if (trimmed.startsWith("CREATE VIEW")) return "CREATE_VIEW";
if (trimmed.startsWith("DROP VIEW")) return "DROP_VIEW";
if (trimmed.startsWith("CREATE TRIGGER")) return "CREATE_TRIGGER";
if (trimmed.startsWith("DROP TRIGGER")) return "DROP_TRIGGER";

return "OTHER";
}
Loading

0 comments on commit feee98a

Please sign in to comment.