refactor packages
This commit is contained in:
@@ -1,16 +0,0 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import { Profile } from '@prisma/client';
|
||||
|
||||
export type IDBEvent = {
|
||||
id: string;
|
||||
name: string;
|
||||
profile_id?: string;
|
||||
project_id: string;
|
||||
properties: Record<string, string>;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export type IDBProfile = Omit<Profile, 'properties'> & {
|
||||
properties: Record<string, unknown>;
|
||||
};
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { IChartEventFilter, IGetChartDataInput } from '@mixan/validation';
|
||||
|
||||
import { formatClickhouseDate } from '../clickhouse-client';
|
||||
import type { SqlBuilderObject } from '../sql-builder';
|
||||
import { createSqlBuilder } from '../sql-builder';
|
||||
@@ -8,8 +10,6 @@ function log(sql: string) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
type IGetChartDataInput = any;
|
||||
|
||||
export function getChartSql({
|
||||
event,
|
||||
breakdowns,
|
||||
@@ -107,7 +107,7 @@ export function getChartSql({
|
||||
|
||||
export function getEventFiltersWhereClause(
|
||||
sb: SqlBuilderObject,
|
||||
filters: any[]
|
||||
filters: IChartEventFilter[]
|
||||
) {
|
||||
filters.forEach((filter, index) => {
|
||||
const id = `f${index}`;
|
||||
|
||||
19
packages/db/src/services/clients.service.ts
Normal file
19
packages/db/src/services/clients.service.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { Client } from '../prisma-client';
|
||||
import { db } from '../prisma-client';
|
||||
import type { IServiceProject } from './project.service';
|
||||
|
||||
export type IServiceClient = Client;
|
||||
export type IServiceClientWithProject = Client & {
|
||||
project: Exclude<IServiceProject, null>;
|
||||
};
|
||||
|
||||
export function getClientsByOrganizationId(organizationId: string) {
|
||||
return db.client.findMany({
|
||||
where: {
|
||||
organization_slug: organizationId,
|
||||
},
|
||||
include: {
|
||||
project: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
50
packages/db/src/services/dashboard.service.ts
Normal file
50
packages/db/src/services/dashboard.service.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { db } from '../prisma-client';
|
||||
|
||||
export type IServiceDashboard = Awaited<ReturnType<typeof getDashboardById>>;
|
||||
export type IServiceDashboards = Awaited<
|
||||
ReturnType<typeof getDashboardsByProjectId>
|
||||
>;
|
||||
|
||||
export async function getDashboardById(id: string) {
|
||||
const dashboard = await db.dashboard.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
include: {
|
||||
project: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!dashboard) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dashboard;
|
||||
}
|
||||
|
||||
export async function getDashboardsByOrganization(organizationSlug: string) {
|
||||
return db.dashboard.findMany({
|
||||
where: {
|
||||
organization_slug: organizationSlug,
|
||||
},
|
||||
include: {
|
||||
project: true,
|
||||
},
|
||||
orderBy: {
|
||||
reports: {
|
||||
_count: 'desc',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function getDashboardsByProjectId(projectId: string) {
|
||||
return db.dashboard.findMany({
|
||||
where: {
|
||||
project_id: projectId,
|
||||
},
|
||||
include: {
|
||||
project: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { randomSplitName, toDots } from '@mixan/common';
|
||||
import { redis, redisPub } from '@mixan/redis';
|
||||
import type { IChartEventFilter } from '@mixan/validation';
|
||||
|
||||
import {
|
||||
ch,
|
||||
@@ -12,9 +13,9 @@ import {
|
||||
} from '../clickhouse-client';
|
||||
import type { EventMeta, Prisma } from '../prisma-client';
|
||||
import { db } from '../prisma-client';
|
||||
import type { IDBProfile } from '../prisma-types';
|
||||
import { createSqlBuilder } from '../sql-builder';
|
||||
import { getEventFiltersWhereClause } from './chart.service';
|
||||
import type { IServiceProfile } from './profile.service';
|
||||
|
||||
export interface IClickhouseEvent {
|
||||
id: string;
|
||||
@@ -40,7 +41,7 @@ export interface IClickhouseEvent {
|
||||
model: string;
|
||||
|
||||
// They do not exist here. Just make ts happy for now
|
||||
profile?: IDBProfile;
|
||||
profile?: IServiceProfile;
|
||||
meta?: EventMeta;
|
||||
}
|
||||
|
||||
@@ -100,7 +101,7 @@ export interface IServiceCreateEventPayload {
|
||||
referrer: string | undefined;
|
||||
referrerName: string | undefined;
|
||||
referrerType: string | undefined;
|
||||
profile: IDBProfile | undefined;
|
||||
profile: IServiceProfile | undefined;
|
||||
meta: EventMeta | undefined;
|
||||
}
|
||||
|
||||
@@ -131,9 +132,7 @@ export async function getEvents(
|
||||
});
|
||||
|
||||
for (const event of events) {
|
||||
event.profile = profiles.find((p) => p.id === event.profile_id) as
|
||||
| IDBProfile
|
||||
| undefined;
|
||||
event.profile = profiles.find((p) => p.id === event.profile_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +250,8 @@ interface GetEventListOptions {
|
||||
profileId?: string;
|
||||
take: number;
|
||||
cursor?: number;
|
||||
filters: any[];
|
||||
events?: string[] | null;
|
||||
filters?: IChartEventFilter[];
|
||||
}
|
||||
|
||||
export async function getEventList({
|
||||
@@ -259,18 +259,29 @@ export async function getEventList({
|
||||
take,
|
||||
projectId,
|
||||
profileId,
|
||||
events,
|
||||
filters,
|
||||
}: GetEventListOptions) {
|
||||
const { sb, getSql } = createSqlBuilder();
|
||||
const { sb, getSql, join } = createSqlBuilder();
|
||||
|
||||
sb.limit = take;
|
||||
sb.offset = (cursor ?? 0) * take;
|
||||
sb.where.projectId = `project_id = '${projectId}'`;
|
||||
|
||||
if (profileId) {
|
||||
sb.where.profileId = `profile_id = '${profileId}'`;
|
||||
}
|
||||
|
||||
getEventFiltersWhereClause(sb, filters);
|
||||
if (events && events.length > 0) {
|
||||
sb.where.events = `name IN (${join(
|
||||
events.map((n) => `'${n}'`),
|
||||
','
|
||||
)})`;
|
||||
}
|
||||
|
||||
if (filters) {
|
||||
getEventFiltersWhereClause(sb, filters);
|
||||
}
|
||||
|
||||
// if (cursor) {
|
||||
// sb.where.cursor = `created_at <= '${formatClickhouseDate(cursor)}'`;
|
||||
@@ -284,15 +295,25 @@ export async function getEventList({
|
||||
export async function getEventsCount({
|
||||
projectId,
|
||||
profileId,
|
||||
events,
|
||||
filters,
|
||||
}: Omit<GetEventListOptions, 'cursor' | 'take'>) {
|
||||
const { sb, getSql } = createSqlBuilder();
|
||||
const { sb, getSql, join } = createSqlBuilder();
|
||||
sb.where.projectId = `project_id = '${projectId}'`;
|
||||
if (profileId) {
|
||||
sb.where.profileId = `profile_id = '${profileId}'`;
|
||||
}
|
||||
|
||||
getEventFiltersWhereClause(sb, filters);
|
||||
if (events && events.length > 0) {
|
||||
sb.where.events = `name IN (${join(
|
||||
events.map((n) => `'${n}'`),
|
||||
','
|
||||
)})`;
|
||||
}
|
||||
|
||||
if (filters) {
|
||||
getEventFiltersWhereClause(sb, filters);
|
||||
}
|
||||
|
||||
const res = await chQuery<{ count: number }>(
|
||||
getSql().replace('*', 'count(*) as count')
|
||||
|
||||
67
packages/db/src/services/organization.service.ts
Normal file
67
packages/db/src/services/organization.service.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { auth, clerkClient } from '@clerk/nextjs';
|
||||
import type {
|
||||
Organization,
|
||||
OrganizationInvitation,
|
||||
} from '@clerk/nextjs/dist/types/server';
|
||||
|
||||
import { db } from '../prisma-client';
|
||||
|
||||
export type IServiceOrganization = Awaited<
|
||||
ReturnType<typeof getCurrentOrganizations>
|
||||
>[number];
|
||||
|
||||
export type IServiceInvites = Awaited<ReturnType<typeof getInvites>>;
|
||||
|
||||
function transformOrganization(org: Organization) {
|
||||
return {
|
||||
id: org.id,
|
||||
name: org.name,
|
||||
slug: org.slug,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getCurrentOrganizations() {
|
||||
const session = auth();
|
||||
const organizations = await clerkClient.users.getOrganizationMembershipList({
|
||||
userId: session.userId!,
|
||||
});
|
||||
return organizations.map((item) => transformOrganization(item.organization));
|
||||
}
|
||||
|
||||
export function getOrganizationBySlug(slug: string) {
|
||||
return clerkClient.organizations
|
||||
.getOrganization({ slug })
|
||||
.then(transformOrganization)
|
||||
.catch(() => null);
|
||||
}
|
||||
|
||||
export async function getOrganizationByProjectId(projectId: string) {
|
||||
const project = await db.project.findUniqueOrThrow({
|
||||
where: {
|
||||
id: projectId,
|
||||
},
|
||||
});
|
||||
|
||||
return clerkClient.organizations.getOrganization({
|
||||
slug: project.organization_slug,
|
||||
});
|
||||
}
|
||||
|
||||
export function transformInvite(invite: OrganizationInvitation) {
|
||||
return {
|
||||
id: invite.id,
|
||||
email: invite.emailAddress,
|
||||
role: invite.role,
|
||||
status: invite.status,
|
||||
createdAt: invite.createdAt,
|
||||
updatedAt: invite.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getInvites(organizationId: string) {
|
||||
return await clerkClient.organizations
|
||||
.getOrganizationInvitationList({
|
||||
organizationId,
|
||||
})
|
||||
.then((invites) => invites.map(transformInvite));
|
||||
}
|
||||
35
packages/db/src/services/profile.service.ts
Normal file
35
packages/db/src/services/profile.service.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { db } from '../prisma-client';
|
||||
|
||||
export type IServiceProfile = Awaited<ReturnType<typeof getProfileById>>;
|
||||
|
||||
export function getProfileById(id: string) {
|
||||
return db.profile.findUniqueOrThrow({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function getProfilesByExternalId(
|
||||
externalId: string | null,
|
||||
projectId: string
|
||||
) {
|
||||
if (externalId === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return db.profile.findMany({
|
||||
where: {
|
||||
external_id: externalId,
|
||||
project_id: projectId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function getProfile(id: string) {
|
||||
return db.profile.findUniqueOrThrow({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
30
packages/db/src/services/project.service.ts
Normal file
30
packages/db/src/services/project.service.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { db } from '../prisma-client';
|
||||
|
||||
export type IServiceProject = Awaited<ReturnType<typeof getProjectById>>;
|
||||
|
||||
export function getProjectById(id: string) {
|
||||
return db.project.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function getProjectsByOrganizationSlug(slug: string) {
|
||||
return db.project.findMany({
|
||||
where: {
|
||||
organization_slug: slug,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getProjectWithMostEvents(slug: string) {
|
||||
return db.project.findFirst({
|
||||
where: {
|
||||
organization_slug: slug,
|
||||
},
|
||||
orderBy: {
|
||||
eventsCount: 'desc',
|
||||
},
|
||||
});
|
||||
}
|
||||
85
packages/db/src/services/reports.service.ts
Normal file
85
packages/db/src/services/reports.service.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { alphabetIds, lineTypes, timeRanges } from '@mixan/constants';
|
||||
import type {
|
||||
IChartBreakdown,
|
||||
IChartEvent,
|
||||
IChartEventFilter,
|
||||
IChartInput,
|
||||
IChartLineType,
|
||||
IChartRange,
|
||||
} from '@mixan/validation';
|
||||
|
||||
import { db } from '../prisma-client';
|
||||
import type { Report as DbReport } from '../prisma-client';
|
||||
|
||||
export type IServiceReport = Awaited<ReturnType<typeof getReportById>>;
|
||||
|
||||
export function transformFilter(
|
||||
filter: Partial<IChartEventFilter>,
|
||||
index: number
|
||||
): IChartEventFilter {
|
||||
return {
|
||||
id: filter.id ?? alphabetIds[index] ?? 'A',
|
||||
name: filter.name ?? 'Unknown Filter',
|
||||
operator: filter.operator ?? 'is',
|
||||
value:
|
||||
typeof filter.value === 'string' ? [filter.value] : filter.value ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
export function transformReportEvent(
|
||||
event: Partial<IChartEvent>,
|
||||
index: number
|
||||
): IChartEvent {
|
||||
return {
|
||||
segment: event.segment ?? 'event',
|
||||
filters: (event.filters ?? []).map(transformFilter),
|
||||
id: event.id ?? alphabetIds[index]!,
|
||||
name: event.name || 'unknown_event',
|
||||
displayName: event.displayName,
|
||||
property: event.property,
|
||||
};
|
||||
}
|
||||
|
||||
export function transformReport(
|
||||
report: DbReport
|
||||
): IChartInput & { id: string } {
|
||||
return {
|
||||
id: report.id,
|
||||
projectId: report.project_id,
|
||||
events: (report.events as IChartEvent[]).map(transformReportEvent),
|
||||
breakdowns: report.breakdowns as IChartBreakdown[],
|
||||
chartType: report.chart_type,
|
||||
lineType: (report.line_type as IChartLineType) ?? lineTypes.monotone,
|
||||
interval: report.interval,
|
||||
name: report.name || 'Untitled',
|
||||
range: (report.range as IChartRange) ?? timeRanges['1m'],
|
||||
previous: report.previous ?? false,
|
||||
formula: report.formula ?? undefined,
|
||||
metric: report.metric ?? 'sum',
|
||||
unit: report.unit ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function getReportsByDashboardId(dashboardId: string) {
|
||||
return db.report
|
||||
.findMany({
|
||||
where: {
|
||||
dashboard_id: dashboardId,
|
||||
},
|
||||
})
|
||||
.then((reports) => reports.map(transformReport));
|
||||
}
|
||||
|
||||
export async function getReportById(id: string) {
|
||||
const report = await db.report.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!report) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return transformReport(report);
|
||||
}
|
||||
24
packages/db/src/services/user.service.ts
Normal file
24
packages/db/src/services/user.service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { auth, clerkClient } from '@clerk/nextjs';
|
||||
import type { User } from '@clerk/nextjs/dist/types/server';
|
||||
|
||||
export function transformUser(user: User) {
|
||||
return {
|
||||
name: `${user.firstName} ${user.lastName}`,
|
||||
email: user.emailAddresses[0]?.emailAddress ?? '',
|
||||
id: user.id,
|
||||
lastName: user.lastName ?? '',
|
||||
firstName: user.firstName ?? '',
|
||||
};
|
||||
}
|
||||
|
||||
export async function getCurrentUser() {
|
||||
const session = auth();
|
||||
if (!session.userId) {
|
||||
return null;
|
||||
}
|
||||
return getUserById(session.userId);
|
||||
}
|
||||
|
||||
export async function getUserById(id: string) {
|
||||
return clerkClient.users.getUser(id).then(transformUser);
|
||||
}
|
||||
Reference in New Issue
Block a user