refactor packages

This commit is contained in:
Carl-Gerhard Lindesvärd
2024-02-19 10:55:15 +01:00
parent ae8482c1e3
commit 2f3c5ddf76
142 changed files with 2234 additions and 5507 deletions

View File

@@ -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}`;

View 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,
},
});
}

View 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,
},
});
}

View File

@@ -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')

View 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));
}

View 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,
},
});
}

View 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',
},
});
}

View 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);
}

View 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);
}