diff --git a/apps/web/src/components/Test/Serves.tsx b/apps/web/src/components/Test/Serves.tsx
index 8934cf37..d00010a5 100644
--- a/apps/web/src/components/Test/Serves.tsx
+++ b/apps/web/src/components/Test/Serves.tsx
@@ -57,28 +57,17 @@ export const OPTIONS: ChartOptions<"bar"> = {
};
const Serves = ({
- pingEvents,
- options,
+ visitData,
}: {
- pingEvents: Event[];
- options: ClientOption[];
+ visitData: (ClientOption & { visitedEventCount: number })[];
}) => {
- const labels = options.map((option) => option.identifier);
-
- const actualData = useMemo(() => {
- return options.map((option) => {
- return pingEvents.filter(
- (event) => event.selectedVariant === option.identifier
- ).length;
- });
- }, [options, pingEvents]);
-
- const absPings = actualData.reduce((accumulator, value) => {
- return accumulator + value;
+ const labels = visitData.map((data) => data.identifier);
+ const absPings = visitData.reduce((accumulator, value) => {
+ return accumulator + value.visitedEventCount;
}, 0);
return (
-
+
parseFloat(option.chance.toString()) * 100
- ),
+ data: visitData.map((data) => {
+ return parseFloat(data.chance.toString()) * 100;
+ }),
backgroundColor: "#A9E4EF",
},
{
label: "Actual",
- data: actualData.map((data) =>
- Math.round((data / absPings) * 100)
- ),
+ data: visitData.map((data) => {
+ return Math.round((data.visitedEventCount / absPings) * 100);
+ }),
backgroundColor: "#f472b6",
},
],
diff --git a/apps/web/src/lib/events.ts b/apps/web/src/lib/events.ts
index bdb5233c..93cb0808 100644
--- a/apps/web/src/lib/events.ts
+++ b/apps/web/src/lib/events.ts
@@ -1,8 +1,9 @@
+import { assertUnreachable } from "@tryabby/core";
import dayjs from "dayjs";
export enum SpecialTimeInterval {
DAY = "day",
- MONTH_TO_DATE = "month",
+ Last30DAYS = "30d",
ALL_TIME = "all",
}
@@ -11,22 +12,10 @@ export const INTERVALS = [
label: "Today",
value: SpecialTimeInterval.DAY,
},
- // {
- // label: "Last 7 days",
- // value: "7d",
- // },
{
label: "Last 30 days",
- value: "30d",
+ value: SpecialTimeInterval.Last30DAYS,
},
- // {
- // label: "Year to Date",
- // value: SpecialTimeInterval.MONTH_TO_DATE,
- // },
- // {
- // label: "Last 12 months",
- // value: "12mo",
- // },
{
label: "All Time",
value: SpecialTimeInterval.ALL_TIME,
@@ -54,12 +43,14 @@ export function getMSFromSpecialTimeInterval(
case SpecialTimeInterval.DAY: {
return 1000 * 60 * 60 * 24;
}
- case SpecialTimeInterval.MONTH_TO_DATE: {
- return new Date().getTime() - new Date().setDate(1);
+ case SpecialTimeInterval.Last30DAYS: {
+ return 1000 * 60 * 60 * 24 * 30;
}
case SpecialTimeInterval.ALL_TIME: {
return Infinity;
}
+ default:
+ assertUnreachable(timeInterval);
}
}
@@ -80,26 +71,41 @@ export function getFormattingByInterval(interval: INTERVAL) {
export function getLabelsByInterval(
interval: (typeof INTERVALS)[number]["value"],
fistEventDate: Date
-): Array {
+): { labels: Array; dates: Array } {
const formatting = getFormattingByInterval(interval);
switch (interval) {
case SpecialTimeInterval.DAY: {
const baseData = dayjs().set("minute", 0);
- return [0, 3, 6, 9, 12, 15, 18, 21].map((hour) =>
- baseData.set("hour", hour).format(formatting)
+ const dateArray = [0, 3, 6, 9, 12, 15, 18, 21].map((hour) =>
+ baseData.set("hour", hour)
);
+ return {
+ labels: dateArray.map((date) => date.format(formatting)),
+ dates: dateArray.map((date) => date.toDate()),
+ };
}
- case "30d": {
- return Array.from({ length: 30 }, (_, i) =>
- dayjs().subtract(i, "day").format(formatting)
+
+ case SpecialTimeInterval.Last30DAYS: {
+ const dateArray = Array.from({ length: 30 }, (_, i) =>
+ dayjs().subtract(i, "day")
).reverse();
+ return {
+ labels: dateArray.map((date) => date.format(formatting)),
+ dates: dateArray.map((date) => date.set("minute", 0).toDate()),
+ };
}
case SpecialTimeInterval.ALL_TIME: {
const diff = dayjs().diff(dayjs(fistEventDate), "month");
- return Array.from({ length: Math.max(diff, 6) }, (_, i) =>
- dayjs(fistEventDate).add(i, "month").format(formatting)
+ const dateArray = Array.from({ length: Math.max(diff, 6) }, (_, i) =>
+ dayjs(fistEventDate).add(i, "month")
).reverse();
+ return {
+ labels: dateArray.map((date) => date.format(formatting)),
+ dates: dateArray.map((date) => date.toDate()),
+ };
}
+ default:
+ return assertUnreachable(interval);
}
}
diff --git a/apps/web/src/pages/projects/[projectId]/index.tsx b/apps/web/src/pages/projects/[projectId]/index.tsx
index fe5c2819..b810a61a 100644
--- a/apps/web/src/pages/projects/[projectId]/index.tsx
+++ b/apps/web/src/pages/projects/[projectId]/index.tsx
@@ -10,6 +10,7 @@ import { AiOutlinePlus } from "react-icons/ai";
import { trpc } from "utils/trpc";
import { Button } from "components/ui/button";
import { GetStaticProps, GetStaticPaths } from "next";
+import { AbbyEventType } from "@tryabby/core";
const Projects: NextPageWithLayout = () => {
const [isCreateTestModalOpen, setIsCreateTestModalOpen] = useState(false);
@@ -58,9 +59,16 @@ const Projects: NextPageWithLayout = () => {
/>
- {data?.project?.tests.map((test) => (
-
- ))}
+ {data?.project?.tests.map((test) => {
+ return (
+
+ );
+ })}
>
);
diff --git a/apps/web/src/pages/projects/[projectId]/tests/[testId].tsx b/apps/web/src/pages/projects/[projectId]/tests/[testId].tsx
index 03b8fcca..8c8bfc03 100644
--- a/apps/web/src/pages/projects/[projectId]/tests/[testId].tsx
+++ b/apps/web/src/pages/projects/[projectId]/tests/[testId].tsx
@@ -29,6 +29,7 @@ import {
Legend,
Filler,
ChartOptions,
+ ChartData,
} from "chart.js";
import colors from "tailwindcss/colors";
import { useMemo } from "react";
@@ -100,8 +101,8 @@ const TestDetailPage: NextPageWithLayout = () => {
enabled: !!testId,
}
);
-
- const { data: events } = trpc.events.getEventsByTestId.useQuery(
+ //TODO beide zusammen fassen
+ const { data } = trpc.events.getEventsByTestId.useQuery(
{
testId,
interval,
@@ -111,70 +112,50 @@ const TestDetailPage: NextPageWithLayout = () => {
}
);
- const eventsByVariant = useMemo(() => {
- const eventsByVariant = groupBy(events, (e) => e.selectedVariant);
- // make sure all variants are present
- test?.options.map((option) => {
- eventsByVariant[option.identifier] ??= [];
- });
- return eventsByVariant;
- }, [events, test?.options]);
+ const events = useMemo(() => data ?? [], [data]);
const labels = getLabelsByInterval(
interval,
- minBy(events, "createdAt")?.createdAt!
+ minBy(events, "createdAt")?.startTime!
);
- const formattedEvents = useMemo(() => {
- return Object.entries(eventsByVariant).map(([variant, events], i) => {
- const eventsByDate = groupBy(events, (e) => {
- const date = dayjs(e.createdAt);
- // round by 3 hours
- const hour = Math.floor(date.hour() / 3) * 3;
-
- return date
- .set("hour", hour)
- .set("minute", 0)
- .format(getFormattingByInterval(interval));
- });
- return { eventsByDate, variant };
- });
- }, [eventsByVariant, interval]);
-
- const viewEvents = useMemo(
+ const viewEvents: ChartData<"line", number[], unknown> = useMemo(
() => ({
- labels,
- datasets: formattedEvents.map(({ eventsByDate, variant }, i) => {
- return {
- data: labels.map(
- (label) =>
- eventsByDate[label]?.filter((e) => e.type === AbbyEventType.PING)
- ?.length ?? 0
- ),
- ...getChartOptions(i, variant),
- };
- }),
+ labelsAndDates: labels.labels,
+ datasets: events
+ .filter((event) => event.type === AbbyEventType.PING)
+ .map((event, i) => {
+ return {
+ data: labels.dates.map((date) => {
+ console.log("Label", date, event.startTime);
+ return events.find((e) => e.startTime === date)?.count ?? 0;
+ }),
+ ...getChartOptions(i, event.selectedVariant),
+ };
+ }),
}),
- [formattedEvents, labels]
+ [events, labels]
);
- const actEvents = useMemo(
+ console.log(viewEvents);
+
+ const actEvents: ChartData<"line", number[], unknown> = useMemo(
() => ({
- labels,
- datasets: formattedEvents.map(({ eventsByDate, variant }, i) => {
- return {
- data: labels.map(
- (label) =>
- eventsByDate[label]?.filter((e) => e.type === AbbyEventType.ACT)
- ?.length ?? 0
- ),
- ...getChartOptions(i, variant),
- };
- }),
+ labels: labels.labels,
+ datasets: events
+ .filter((event) => event.type === AbbyEventType.ACT)
+ .map((event, i) => {
+ return {
+ data: labels.dates.map((date) => event.count),
+ ...getChartOptions(i, event.selectedVariant),
+ };
+ }),
}),
- [formattedEvents, labels]
+ [events, labels]
);
+ if (!events) return
;
+
if (isTestLoading || isTestError) {
return
;
}
diff --git a/apps/web/src/server/db/clickhouseClient.ts b/apps/web/src/server/db/clickhouseClient.ts
new file mode 100644
index 00000000..5032801b
--- /dev/null
+++ b/apps/web/src/server/db/clickhouseClient.ts
@@ -0,0 +1,14 @@
+import { NodeClickHouseClient } from "@clickhouse/client/dist/client";
+import { env } from "../../env/server.mjs";
+import { createClient } from "@clickhouse/client";
+
+declare global {
+ // eslint-disable-next-line no-var
+ var clickhouseClient: NodeClickHouseClient | undefined;
+}
+
+export const clickhouseClient = global.clickhouseClient || createClient();
+
+if (env.NODE_ENV !== "production") {
+ global.clickhouseClient = clickhouseClient;
+}
diff --git a/apps/web/src/server/queue/AfterDataRequest.ts b/apps/web/src/server/queue/AfterDataRequest.ts
index 7a91b874..0392a0ae 100644
--- a/apps/web/src/server/queue/AfterDataRequest.ts
+++ b/apps/web/src/server/queue/AfterDataRequest.ts
@@ -3,9 +3,9 @@ import { ApiVersion } from "@prisma/client";
import { trackPlanOverage } from "lib/logsnag";
import { RequestCache } from "server/services/RequestCache";
import { RequestService } from "server/services/RequestService";
-import { EventService } from "server/services/EventService";
import { afterDataRequestQueue, getQueueingRedisConnection } from "./queues";
import { env } from "env/server.mjs";
+import { ClickHouseEventService } from "server/services/ClickHouseEventService";
export type AfterRequestJobPayload = {
functionDuration: number;
@@ -17,7 +17,7 @@ const afterDataRequestWorker = new Worker
(
afterDataRequestQueue.name,
async ({ data: { apiVersion, functionDuration, projectId } }) => {
const { events, planLimits, plan, is80PercentOfLimit } =
- await EventService.getEventsForCurrentPeriod(projectId);
+ await ClickHouseEventService.getEventsForCurrentPeriod(projectId);
if (events > planLimits.eventsPerMonth) {
// TODO: send email
diff --git a/apps/web/src/server/queue/event.ts b/apps/web/src/server/queue/event.ts
index 3215e6a2..ab825e85 100644
--- a/apps/web/src/server/queue/event.ts
+++ b/apps/web/src/server/queue/event.ts
@@ -2,11 +2,11 @@ import { Worker } from "bullmq";
import { trackPlanOverage } from "lib/logsnag";
import { RequestCache } from "server/services/RequestCache";
import { RequestService } from "server/services/RequestService";
-import { EventService } from "server/services/EventService";
import { eventQueue, getQueueingRedisConnection } from "./queues";
import { AbbyEvent, AbbyEventType } from "@tryabby/core";
import { env } from "env/server.mjs";
import { ApiRequestType } from "@prisma/client";
+import { ClickHouseEventService } from "server/services/ClickHouseEventService";
export type EventJobPayload = AbbyEvent & {
functionDuration: number;
@@ -24,15 +24,18 @@ const eventWorker = new Worker(
switch (event.type) {
case AbbyEventType.PING:
case AbbyEventType.ACT: {
- await EventService.createEvent(event);
+ await ClickHouseEventService.createEvent(event);
+
break;
}
default: {
event.type satisfies never;
}
}
+
+ //could be moved into a cron job and checked only once a hour
const { events, planLimits, plan, is80PercentOfLimit } =
- await EventService.getEventsForCurrentPeriod(event.projectId);
+ await ClickHouseEventService.getEventsForCurrentPeriod(event.projectId);
if (events > planLimits.eventsPerMonth) {
// TODO: send email
diff --git a/apps/web/src/server/services/ClickHouseEventService.ts b/apps/web/src/server/services/ClickHouseEventService.ts
new file mode 100644
index 00000000..a5052dc5
--- /dev/null
+++ b/apps/web/src/server/services/ClickHouseEventService.ts
@@ -0,0 +1,200 @@
+import { SpecialTimeInterval } from "lib/events";
+import { getLimitByPlan, PlanName, PLANS } from "server/common/plans";
+import { prisma } from "server/db/client";
+import { AbbyEvent, AbbyEventType, assertUnreachable } from "@tryabby/core";
+import { RequestCache } from "./RequestCache";
+import { clickhouseClient } from "server/db/clickhouseClient";
+import { z } from "zod";
+import dayjs from "dayjs";
+
+const GroupedTestQueryResultSchema = z.object({
+ selectedVariant: z.string(),
+ type: z.number(),
+ count: z.string(),
+});
+
+const GroupedTestQueryResultSchemaWithTimeSchema = z.intersection(
+ GroupedTestQueryResultSchema,
+ z.object({ startTime: z.string() })
+);
+
+const EventCurrentPeriodQueryResultSchema = z.object({
+ apiRequestCount: z.string(),
+});
+
+type GroupedTestQueryResultSchemaWithTime = z.infer<
+ typeof GroupedTestQueryResultSchemaWithTimeSchema
+>;
+type GroupedTestQueryResult = z.infer;
+
+export abstract class ClickHouseEventService {
+ static async createEvent({
+ projectId,
+ selectedVariant,
+ testName,
+ type,
+ }: AbbyEvent) {
+ const insertedEvent = await clickhouseClient.insert({
+ table: "abby.Event",
+ format: "JSONEachRow",
+ values: [
+ {
+ project_id: projectId,
+ testName: testName,
+ type,
+ selectedVariant: selectedVariant,
+ },
+ ],
+ });
+
+ return insertedEvent;
+ }
+
+ static async getEventsByProjectId(projectId: string): Promise<
+ {
+ id: string;
+ testId: string;
+ type: number;
+ selectedVariant: string;
+ createdAt: Date;
+ }[]
+ > {
+ const queryResult = await clickhouseClient.query({
+ query: `SELECT * FROM abby.events WHERE projectId = '${projectId}'`,
+ });
+
+ return (await queryResult.json()).data as any;
+ }
+
+ static async getGroupedEventsByTestId(
+ test: {
+ options: {
+ id: string;
+ identifier: string;
+ testId: string;
+ }[];
+ } & {
+ id: string;
+ projectId: string;
+ createdAt: Date;
+ updatedAt: Date;
+ name: string;
+ }
+ ) {
+ const queryResult = await clickhouseClient.query({
+ query: `select count(*) as count, type, selectedVariant from abby.Event
+ where testName ='${test.id}'
+ group by type, selectedVariant;
+ `,
+ });
+ const parsedJson = (await queryResult.json()).data;
+
+ return parsedJson.map((row) => {
+ const { count, selectedVariant, type } =
+ GroupedTestQueryResultSchema.parse(row);
+ return {
+ variant: selectedVariant,
+ type: type === 0 ? AbbyEventType.PING : AbbyEventType.ACT,
+ count: parseInt(count),
+ };
+ });
+ }
+
+ //brauchen wir das?
+ static async getEventsByTestId(
+ testId: string,
+ timeInterval: SpecialTimeInterval
+ ) {
+ const computedBucketSize = this.computeBucketSize(timeInterval);
+
+ try {
+ const result = await clickhouseClient.query({
+ query: `
+ SELECT
+ ${computedBucketSize} AS startTime,
+ Count(selectedVariant) AS count,
+ selectedVariant,
+ type
+ FROM abby.Event
+ WHERE testName = '${testId}'
+ GROUP BY startTime, selectedVariant, type
+ ORDER BY startTime ASC;
+`,
+ });
+
+ const parsedJson = (await result.json()).data;
+ console.log(parsedJson);
+ const parsedRes = parsedJson.map((row) => {
+ const { count, selectedVariant, type, startTime } =
+ GroupedTestQueryResultSchemaWithTimeSchema.parse(row);
+ return {
+ startTime: new Date(startTime),
+ selectedVariant,
+ type: type === 0 ? AbbyEventType.PING : AbbyEventType.ACT,
+ count: parseInt(count),
+ };
+ });
+
+ return parsedRes;
+ } catch (e) {
+ console.log("error", e);
+ }
+ }
+
+ static async getEventsForCurrentPeriod(projectId: string) {
+ const [project, eventCount] = await Promise.all([
+ prisma.project.findUnique({
+ where: { id: projectId },
+ select: { stripePriceId: true, currentPeriodEnd: true },
+ }),
+ RequestCache.get(projectId),
+ ]);
+
+ if (!project) throw new Error("Project not found");
+
+ const billingPeriodStartDate = dayjs(project.currentPeriodEnd)
+ .subtract(30, "days")
+ .format("YYYY-MM-DD");
+
+ const res = await clickhouseClient
+ .query({
+ query: `
+ SELECT
+ Count(*) as apiRequestCount
+ FROM abby.ApiRequest
+ WHERE projectId = '${projectId}' AND createdAt >= toDate('${billingPeriodStartDate}');
+
+`,
+ })
+ .then((res) => res.json());
+
+ const plan = Object.keys(PLANS).find(
+ (plan) => PLANS[plan as PlanName] === project.stripePriceId
+ ) as PlanName | undefined;
+
+ const planLimits = getLimitByPlan(plan ?? null);
+
+ return {
+ events: parseInt(
+ EventCurrentPeriodQueryResultSchema.parse(res.data[0]).apiRequestCount
+ ),
+ planLimits,
+ plan,
+ is80PercentOfLimit: planLimits.eventsPerMonth * 0.8 === eventCount,
+ };
+ }
+
+ static computeBucketSize(timeInterval: SpecialTimeInterval) {
+ switch (timeInterval) {
+ case SpecialTimeInterval.DAY: {
+ return "toStartOfHour(createdAt)";
+ }
+ case SpecialTimeInterval.ALL_TIME:
+ case SpecialTimeInterval.Last30DAYS: {
+ return "toStartOfDay(createdAt)";
+ }
+ default:
+ return assertUnreachable(timeInterval);
+ }
+ }
+}
diff --git a/apps/web/src/server/services/EventService.ts b/apps/web/src/server/services/EventService.ts
deleted file mode 100644
index dba9ace5..00000000
--- a/apps/web/src/server/services/EventService.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import dayjs from "dayjs";
-import {
- getMSFromSpecialTimeInterval,
- isSpecialTimeInterval,
- SpecialTimeInterval,
-} from "lib/events";
-import ms from "ms";
-import { getLimitByPlan, PlanName, PLANS } from "server/common/plans";
-import { prisma } from "server/db/client";
-import { AbbyEvent } from "@tryabby/core";
-import { RequestCache } from "./RequestCache";
-
-export abstract class EventService {
- static async createEvent({
- projectId,
- selectedVariant,
- testName,
- type,
- }: AbbyEvent) {
- return prisma.event.create({
- data: {
- selectedVariant,
- type,
- test: {
- connect: {
- projectId_name: {
- projectId,
- name: testName,
- },
- },
- },
- },
- });
- }
-
- static async getEventsByProjectId(projectId: string) {
- return prisma.event.findMany({
- where: {
- test: {
- projectId,
- },
- },
- });
- }
-
- static async getEventsByTestId(testId: string, timeInterval: string) {
- const now = new Date().getTime();
-
- if (isSpecialTimeInterval(timeInterval)) {
- const specialIntervalInMs = getMSFromSpecialTimeInterval(timeInterval);
- return prisma.event.findMany({
- where: {
- testId,
- ...(specialIntervalInMs !== Infinity &&
- timeInterval !== SpecialTimeInterval.DAY && {
- createdAt: {
- gte: new Date(now - getMSFromSpecialTimeInterval(timeInterval)),
- },
- }),
- // Special case for day, since we want to include the current day
- ...(timeInterval === SpecialTimeInterval.DAY && {
- createdAt: {
- gte: dayjs().startOf("day").toDate(),
- },
- }),
- },
- });
- }
-
- const parsedInterval = ms(timeInterval) as number | undefined;
-
- if (parsedInterval === undefined) {
- throw new Error("Invalid time interval");
- }
-
- return prisma.event.findMany({
- where: {
- testId,
- createdAt: {
- gte: new Date(now - ms(timeInterval)),
- },
- },
- });
- }
-
- static async getEventsForCurrentPeriod(projectId: string) {
- const [project, eventCount] = await Promise.all([
- prisma.project.findUnique({
- where: { id: projectId },
- select: { stripePriceId: true },
- }),
- RequestCache.get(projectId),
- ]);
-
- if (!project) throw new Error("Project not found");
-
- const plan = Object.keys(PLANS).find(
- (plan) => PLANS[plan as PlanName] === project.stripePriceId
- ) as PlanName | undefined;
-
- const planLimits = getLimitByPlan(plan ?? null);
-
- return {
- events: eventCount,
- planLimits,
- plan,
- is80PercentOfLimit: planLimits.eventsPerMonth * 0.8 === eventCount,
- };
- }
-}
diff --git a/apps/web/src/server/services/RequestService.ts b/apps/web/src/server/services/RequestService.ts
index 79a11f92..f16da152 100644
--- a/apps/web/src/server/services/RequestService.ts
+++ b/apps/web/src/server/services/RequestService.ts
@@ -1,12 +1,24 @@
import { ApiRequest } from "@prisma/client";
import { prisma } from "server/db/client";
+import { clickhouseClient } from "server/db/clickhouseClient";
export abstract class RequestService {
static async storeRequest(request: Omit) {
- await prisma.apiRequest.create({
- data: {
- ...request,
- },
- });
+ const {} = await Promise.all([
+ await prisma.apiRequest.create({
+ data: {
+ ...request,
+ },
+ }),
+ await clickhouseClient.insert({
+ table: "abby.Event",
+ format: "JSONEachRow",
+ values: [
+ {
+ project_id: request.projectId,
+ },
+ ],
+ }),
+ ]);
}
}
diff --git a/apps/web/src/server/trpc/router/events.ts b/apps/web/src/server/trpc/router/events.ts
index 991c601d..cf09aca7 100644
--- a/apps/web/src/server/trpc/router/events.ts
+++ b/apps/web/src/server/trpc/router/events.ts
@@ -1,8 +1,9 @@
import { TRPCError } from "@trpc/server";
-import { EventService } from "server/services/EventService";
import { ProjectService } from "server/services/ProjectService";
import { z } from "zod";
import { protectedProcedure, router } from "../trpc";
+import { ClickHouseEventService } from "server/services/ClickHouseEventService";
+import { SpecialTimeInterval } from "lib/events";
export const eventRouter = router({
getEvents: protectedProcedure
@@ -17,13 +18,13 @@ export const eventRouter = router({
throw new TRPCError({ code: "UNAUTHORIZED" });
}
- return EventService.getEventsByProjectId(input.projectId);
+ return ClickHouseEventService.getEventsByProjectId(input.projectId);
}),
getEventsByTestId: protectedProcedure
.input(
z.object({
testId: z.string(),
- interval: z.string(),
+ interval: z.nativeEnum(SpecialTimeInterval),
})
)
.query(async ({ ctx, input }) => {
@@ -44,11 +45,11 @@ export const eventRouter = router({
throw new TRPCError({ code: "UNAUTHORIZED" });
}
- const tests = await EventService.getEventsByTestId(
+ const clickhouseEvents = await ClickHouseEventService.getEventsByTestId(
input.testId,
input.interval
);
- return tests;
+ return clickhouseEvents;
}),
});
diff --git a/apps/web/src/server/trpc/router/project.ts b/apps/web/src/server/trpc/router/project.ts
index 408c73f7..3b4787a1 100644
--- a/apps/web/src/server/trpc/router/project.ts
+++ b/apps/web/src/server/trpc/router/project.ts
@@ -2,10 +2,9 @@ import { Option, ROLE } from "@prisma/client";
import { TRPCError } from "@trpc/server";
import { PLANS, planNameSchema } from "server/common/plans";
import { stripe } from "server/common/stripe";
-import { EventService } from "server/services/EventService";
import { ProjectService } from "server/services/ProjectService";
import { generateCodeSnippets } from "utils/snippets";
-import { z } from "zod";
+import { ParseStatus, z } from "zod";
export type ClientOption = Omit