Fix linting errors

This commit is contained in:
Dries Augustyns
2025-12-04 12:46:39 +01:00
parent 73a1cdfefa
commit 04634a447b
34 changed files with 517 additions and 534 deletions
+136 -138
View File
@@ -1,5 +1,6 @@
import type {Event} from '@plunk/db';
import {Prisma} from '@plunk/db';
import type {FilterCondition, FilterGroup} from '@plunk/types';
import {prisma} from '../database/prisma.js';
import {redis} from '../database/redis.js';
@@ -177,6 +178,139 @@ export class EventService {
return Array.from(fieldSet).sort();
}
/**
* Check if an event is used in any segments or workflows
* Returns usage information including which segments/workflows use the event
*
* @param projectId - The project ID
* @param eventName - The event name to check (e.g., "purchase.completed", "user.signup")
* @returns Usage information
*/
public static async getEventUsage(
projectId: string,
eventName: string,
): Promise<{
usedInSegments: Array<{id: string; name: string}>;
usedInWorkflows: Array<{id: string; name: string}>;
totalCount: number;
uniqueContacts: number;
canDelete: boolean;
}> {
// Get all segments for the project
const segments = await prisma.segment.findMany({
where: {projectId},
select: {id: true, name: true, condition: true},
});
// Check which segments use this event
const usedInSegments = segments.filter(segment => {
const condition = segment.condition as FilterCondition | null;
return this.eventUsedInCondition(eventName, condition);
});
// Get workflows that use this event as a trigger or wait condition
const workflows = await prisma.workflow.findMany({
where: {
projectId,
OR: [
// Event as trigger
{
triggerType: 'EVENT',
triggerConfig: {
path: ['eventName'],
equals: eventName,
},
},
],
},
select: {id: true, name: true},
});
// Also check workflow steps that wait for events
const workflowStepsWithEvent = await prisma.workflowStep.findMany({
where: {
workflow: {projectId},
type: 'WAIT_FOR_EVENT',
config: {
path: ['eventName'],
equals: eventName,
},
},
include: {
workflow: {
select: {id: true, name: true},
},
},
});
const usedInWorkflows = [...workflows, ...workflowStepsWithEvent.map(step => step.workflow)].reduce(
(acc, workflow) => {
// Deduplicate by id
if (!acc.find((w: {id: string; name: string}) => w.id === workflow.id)) {
acc.push(workflow);
}
return acc;
},
[] as Array<{id: string; name: string}>,
);
// Get event statistics
const [totalCount, uniqueContacts] = await Promise.all([
prisma.event.count({
where: {projectId, name: eventName},
}),
prisma.event
.groupBy({
by: ['contactId'],
where: {projectId, name: eventName, contactId: {not: null}},
})
.then(results => results.length),
]);
const canDelete = usedInSegments.length === 0 && usedInWorkflows.length === 0;
return {
usedInSegments,
usedInWorkflows,
totalCount,
uniqueContacts,
canDelete,
};
}
/**
* Delete all events with a specific name
* WARNING: This is destructive and cannot be undone
* Should only be called after verifying the event is not in use
*
* @param projectId - The project ID
* @param eventName - The event name to delete
*/
public static async deleteEvent(projectId: string, eventName: string): Promise<{deletedCount: number}> {
// Prevent deletion of system events
if (eventName.startsWith('email.') || eventName.startsWith('segment.')) {
throw new Error('Cannot delete system events (email.* or segment.*)');
}
// Check if event is in use
const usage = await this.getEventUsage(projectId, eventName);
if (!usage.canDelete) {
throw new Error(
`Cannot delete event: used in ${usage.usedInSegments.length} segment(s) and ${usage.usedInWorkflows.length} workflow(s)`,
);
}
// Delete all events with this name
const result = await prisma.event.deleteMany({
where: {
projectId,
name: eventName,
},
});
return {deletedCount: result.count};
}
/**
* Trigger workflows based on an event
* Uses Redis caching for enabled workflows to improve performance
@@ -321,113 +455,10 @@ export class EventService {
}
}
/**
* Check if an event is used in any segments or workflows
* Returns usage information including which segments/workflows use the event
*
* @param projectId - The project ID
* @param eventName - The event name to check (e.g., "purchase.completed", "user.signup")
* @returns Usage information
*/
public static async getEventUsage(
projectId: string,
eventName: string,
): Promise<{
usedInSegments: Array<{id: string; name: string}>;
usedInWorkflows: Array<{id: string; name: string}>;
totalCount: number;
uniqueContacts: number;
canDelete: boolean;
}> {
// Get all segments for the project
const segments = await prisma.segment.findMany({
where: {projectId},
select: {id: true, name: true, condition: true},
});
// Check which segments use this event
const usedInSegments = segments.filter(segment => {
const condition = segment.condition as any;
return this.eventUsedInCondition(eventName, condition);
});
// Get workflows that use this event as a trigger or wait condition
const workflows = await prisma.workflow.findMany({
where: {
projectId,
OR: [
// Event as trigger
{
triggerType: 'EVENT',
triggerConfig: {
path: ['eventName'],
equals: eventName,
},
},
],
},
select: {id: true, name: true},
});
// Also check workflow steps that wait for events
const workflowStepsWithEvent = await prisma.workflowStep.findMany({
where: {
workflow: {projectId},
type: 'WAIT_FOR_EVENT',
config: {
path: ['eventName'],
equals: eventName,
},
},
include: {
workflow: {
select: {id: true, name: true},
},
},
});
const usedInWorkflows = [
...workflows,
...workflowStepsWithEvent.map(step => step.workflow),
].reduce(
(acc, workflow) => {
// Deduplicate by id
if (!acc.find((w: {id: string; name: string}) => w.id === workflow.id)) {
acc.push(workflow);
}
return acc;
},
[] as Array<{id: string; name: string}>,
);
// Get event statistics
const [totalCount, uniqueContacts] = await Promise.all([
prisma.event.count({
where: {projectId, name: eventName},
}),
prisma.event
.groupBy({
by: ['contactId'],
where: {projectId, name: eventName, contactId: {not: null}},
})
.then(results => results.length),
]);
const canDelete = usedInSegments.length === 0 && usedInWorkflows.length === 0;
return {
usedInSegments,
usedInWorkflows,
totalCount,
uniqueContacts,
canDelete,
};
}
/**
* Helper: Check if an event is used in a filter condition (recursive)
*/
private static eventUsedInCondition(eventName: string, condition: any): boolean {
private static eventUsedInCondition(eventName: string, condition: FilterCondition | null): boolean {
if (!condition || typeof condition !== 'object') {
return false;
}
@@ -447,7 +478,7 @@ export class EventService {
/**
* Helper: Check if an event is used in a filter group (recursive)
*/
private static eventUsedInGroup(eventName: string, group: any): boolean {
private static eventUsedInGroup(eventName: string, group: FilterGroup): boolean {
if (!group || typeof group !== 'object') {
return false;
}
@@ -469,37 +500,4 @@ export class EventService {
return false;
}
/**
* Delete all events with a specific name
* WARNING: This is destructive and cannot be undone
* Should only be called after verifying the event is not in use
*
* @param projectId - The project ID
* @param eventName - The event name to delete
*/
public static async deleteEvent(projectId: string, eventName: string): Promise<{deletedCount: number}> {
// Prevent deletion of system events
if (eventName.startsWith('email.') || eventName.startsWith('segment.')) {
throw new Error('Cannot delete system events (email.* or segment.*)');
}
// Check if event is in use
const usage = await this.getEventUsage(projectId, eventName);
if (!usage.canDelete) {
throw new Error(
`Cannot delete event: used in ${usage.usedInSegments.length} segment(s) and ${usage.usedInWorkflows.length} workflow(s)`,
);
}
// Delete all events with this name
const result = await prisma.event.deleteMany({
where: {
projectId,
name: eventName,
},
});
return {deletedCount: result.count};
}
}