mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
refactor(core): Decouple projects telemetry (no-changelog) (#10081)
This commit is contained in:
parent
ab5688c582
commit
8b2f76b92e
|
@ -16,7 +16,7 @@ import { InstanceSettings } from 'n8n-core';
|
|||
import config from '@/config';
|
||||
import { N8N_VERSION } from '@/constants';
|
||||
import type { AuthProviderType } from '@db/entities/AuthIdentity';
|
||||
import type { GlobalRole, User } from '@db/entities/User';
|
||||
import type { User } from '@db/entities/User';
|
||||
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
||||
import { WorkflowRepository } from '@db/repositories/workflow.repository';
|
||||
import { determineFinalExecutionStatus } from '@/executionLifecycleHooks/shared/sharedHookFunctions';
|
||||
|
@ -30,7 +30,6 @@ import { EventsService } from '@/services/events.service';
|
|||
import { NodeTypes } from '@/NodeTypes';
|
||||
import { Telemetry } from '@/telemetry';
|
||||
import type { Project } from '@db/entities/Project';
|
||||
import type { ProjectRole } from '@db/entities/ProjectRelation';
|
||||
import { ProjectRelationRepository } from './databases/repositories/projectRelation.repository';
|
||||
import { SharedCredentialsRepository } from './databases/repositories/sharedCredentials.repository';
|
||||
import { MessageEventBus } from './eventbus/MessageEventBus/MessageEventBus';
|
||||
|
@ -834,31 +833,4 @@ export class InternalHooks {
|
|||
}): Promise<void> {
|
||||
return await this.telemetry.track('User updated external secrets settings', saveData);
|
||||
}
|
||||
|
||||
async onTeamProjectCreated(data: { user_id: string; role: GlobalRole }) {
|
||||
return await this.telemetry.track('User created project', data);
|
||||
}
|
||||
|
||||
async onTeamProjectDeleted(data: {
|
||||
user_id: string;
|
||||
role: GlobalRole;
|
||||
project_id: string;
|
||||
removal_type: 'delete' | 'transfer';
|
||||
target_project_id?: string;
|
||||
}) {
|
||||
return await this.telemetry.track('User deleted project', data);
|
||||
}
|
||||
|
||||
async onTeamProjectUpdated(data: {
|
||||
user_id: string;
|
||||
role: GlobalRole;
|
||||
project_id: string;
|
||||
members: Array<{ user_id: string; role: ProjectRole }>;
|
||||
}) {
|
||||
return await this.telemetry.track('Project settings updated', data);
|
||||
}
|
||||
|
||||
async onConcurrencyLimitHit({ threshold }: { threshold: number }) {
|
||||
await this.telemetry.track('User hit concurrency limit', { threshold });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import { initExpressionEvaluator } from '@/ExpressionEvaluator';
|
|||
import { generateHostInstanceId } from '@db/utils/generators';
|
||||
import { WorkflowHistoryManager } from '@/workflows/workflowHistory/workflowHistoryManager.ee';
|
||||
import { ShutdownService } from '@/shutdown/Shutdown.service';
|
||||
import { TelemetryEventRelay } from '@/telemetry/telemetry-event-relay.service';
|
||||
|
||||
export abstract class BaseCommand extends Command {
|
||||
protected logger = Container.get(Logger);
|
||||
|
@ -111,6 +112,7 @@ export abstract class BaseCommand extends Command {
|
|||
|
||||
await Container.get(PostHogClient).init();
|
||||
await Container.get(InternalHooks).init();
|
||||
await Container.get(TelemetryEventRelay).init();
|
||||
}
|
||||
|
||||
protected setInstanceType(instanceType: N8nInstanceType) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import { ProjectRepository } from '@/databases/repositories/project.repository';
|
|||
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
|
||||
import { In, Not } from '@n8n/typeorm';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import { EventRelay } from '@/eventbus/event-relay.service';
|
||||
|
||||
@RestController('/projects')
|
||||
export class ProjectController {
|
||||
|
@ -31,7 +31,7 @@ export class ProjectController {
|
|||
private readonly projectsService: ProjectService,
|
||||
private readonly roleService: RoleService,
|
||||
private readonly projectRepository: ProjectRepository,
|
||||
private readonly internalHooks: InternalHooks,
|
||||
private readonly eventRelay: EventRelay,
|
||||
) {}
|
||||
|
||||
@Get('/')
|
||||
|
@ -52,8 +52,8 @@ export class ProjectController {
|
|||
try {
|
||||
const project = await this.projectsService.createTeamProject(req.body.name, req.user);
|
||||
|
||||
void this.internalHooks.onTeamProjectCreated({
|
||||
user_id: req.user.id,
|
||||
this.eventRelay.emit('team-project-created', {
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
});
|
||||
|
||||
|
@ -195,11 +195,11 @@ export class ProjectController {
|
|||
throw e;
|
||||
}
|
||||
|
||||
void this.internalHooks.onTeamProjectUpdated({
|
||||
user_id: req.user.id,
|
||||
this.eventRelay.emit('team-project-updated', {
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
members: req.body.relations.map(({ userId, role }) => ({ user_id: userId, role })),
|
||||
project_id: req.params.projectId,
|
||||
members: req.body.relations,
|
||||
projectId: req.params.projectId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -211,12 +211,12 @@ export class ProjectController {
|
|||
migrateToProject: req.query.transferId,
|
||||
});
|
||||
|
||||
void this.internalHooks.onTeamProjectDeleted({
|
||||
user_id: req.user.id,
|
||||
this.eventRelay.emit('team-project-deleted', {
|
||||
userId: req.user.id,
|
||||
role: req.user.role,
|
||||
project_id: req.params.projectId,
|
||||
removal_type: req.query.transferId !== undefined ? 'transfer' : 'delete',
|
||||
target_project_id: req.query.transferId,
|
||||
projectId: req.params.projectId,
|
||||
removalType: req.query.transferId !== undefined ? 'transfer' : 'delete',
|
||||
targetProjectId: req.query.transferId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import type { AuthenticationMethod, IWorkflowBase } from 'n8n-workflow';
|
||||
import type { IWorkflowExecutionDataProcess } from '@/Interfaces';
|
||||
import type { ProjectRole } from '@/databases/entities/ProjectRelation';
|
||||
import type { GlobalRole } from '@/databases/entities/User';
|
||||
|
||||
export type UserLike = {
|
||||
id: string;
|
||||
|
@ -10,7 +12,7 @@ export type UserLike = {
|
|||
};
|
||||
|
||||
/**
|
||||
* Events sent by services and consumed by relays, e.g. `AuditEventRelay`.
|
||||
* Events sent by services and consumed by relays, e.g. `AuditEventRelay` and `TelemetryEventRelay`.
|
||||
*/
|
||||
export type Event = {
|
||||
'workflow-created': {
|
||||
|
@ -190,4 +192,27 @@ export type Event = {
|
|||
'execution-started-during-bootup': {
|
||||
executionId: string;
|
||||
};
|
||||
|
||||
'team-project-updated': {
|
||||
userId: string;
|
||||
role: GlobalRole;
|
||||
members: Array<{
|
||||
userId: string;
|
||||
role: ProjectRole;
|
||||
}>;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
'team-project-deleted': {
|
||||
userId: string;
|
||||
role: GlobalRole;
|
||||
projectId: string;
|
||||
removalType: 'transfer' | 'delete';
|
||||
targetProjectId?: string;
|
||||
};
|
||||
|
||||
'team-project-created': {
|
||||
userId: string;
|
||||
role: GlobalRole;
|
||||
};
|
||||
};
|
||||
|
|
60
packages/cli/src/telemetry/telemetry-event-relay.service.ts
Normal file
60
packages/cli/src/telemetry/telemetry-event-relay.service.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { Service } from 'typedi';
|
||||
import { EventRelay } from '@/eventbus/event-relay.service';
|
||||
import type { Event } from '@/eventbus/event.types';
|
||||
import { Telemetry } from '.';
|
||||
import config from '@/config';
|
||||
|
||||
@Service()
|
||||
export class TelemetryEventRelay {
|
||||
constructor(
|
||||
private readonly eventRelay: EventRelay,
|
||||
private readonly telemetry: Telemetry,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
if (!config.getEnv('diagnostics.enabled')) return;
|
||||
|
||||
await this.telemetry.init();
|
||||
|
||||
this.setupHandlers();
|
||||
}
|
||||
|
||||
private setupHandlers() {
|
||||
this.eventRelay.on('team-project-updated', (event) => this.teamProjectUpdated(event));
|
||||
this.eventRelay.on('team-project-deleted', (event) => this.teamProjectDeleted(event));
|
||||
this.eventRelay.on('team-project-created', (event) => this.teamProjectCreated(event));
|
||||
}
|
||||
|
||||
private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) {
|
||||
void this.telemetry.track('Project settings updated', {
|
||||
user_id: userId,
|
||||
role,
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
members: members.map(({ userId: user_id, role }) => ({ user_id, role })),
|
||||
project_id: projectId,
|
||||
});
|
||||
}
|
||||
|
||||
private teamProjectDeleted({
|
||||
userId,
|
||||
role,
|
||||
projectId,
|
||||
removalType,
|
||||
targetProjectId,
|
||||
}: Event['team-project-deleted']) {
|
||||
void this.telemetry.track('User deleted project', {
|
||||
user_id: userId,
|
||||
role,
|
||||
project_id: projectId,
|
||||
removal_type: removalType,
|
||||
target_project_id: targetProjectId,
|
||||
});
|
||||
}
|
||||
|
||||
private teamProjectCreated({ userId, role }: Event['team-project-created']) {
|
||||
void this.telemetry.track('User created project', {
|
||||
user_id: userId,
|
||||
role,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ import { mock } from 'jest-mock-extended';
|
|||
|
||||
import type { BaseCommand } from '@/commands/BaseCommand';
|
||||
import * as testDb from '../testDb';
|
||||
import { TelemetryEventRelay } from '@/telemetry/telemetry-event-relay.service';
|
||||
import { mockInstance } from '@test/mocking';
|
||||
|
||||
export const setupTestCommand = <T extends BaseCommand>(Command: Class<T>) => {
|
||||
const config = mock<Config>();
|
||||
|
@ -19,6 +21,7 @@ export const setupTestCommand = <T extends BaseCommand>(Command: Class<T>) => {
|
|||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockInstance(TelemetryEventRelay);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
|
Loading…
Reference in a new issue