mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-23 11:44:06 -08:00
refactor(core): Decouple user events from internal hooks (no-changelog) (#10292)
This commit is contained in:
parent
88086a41ff
commit
c0f3693e8a
|
@ -267,14 +267,6 @@ export interface IWebhookManager {
|
|||
executeWebhook(req: WebhookRequest, res: Response): Promise<IResponseCallbackData>;
|
||||
}
|
||||
|
||||
export interface ITelemetryUserDeletionData {
|
||||
user_id: string;
|
||||
target_user_old_status: 'active' | 'invited';
|
||||
migration_strategy?: 'transfer_data' | 'delete_data';
|
||||
target_user_id?: string;
|
||||
migration_user_id?: string;
|
||||
}
|
||||
|
||||
export interface IVersionNotificationSettings {
|
||||
enabled: boolean;
|
||||
endpoint: string;
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { Service } from 'typedi';
|
||||
import { snakeCase } from 'change-case';
|
||||
import type { ITelemetryTrackProperties } from 'n8n-workflow';
|
||||
import type { AuthProviderType } from '@db/entities/AuthIdentity';
|
||||
import type { User } from '@db/entities/User';
|
||||
import type { ITelemetryUserDeletionData } from '@/Interfaces';
|
||||
import { WorkflowStatisticsService } from '@/services/workflow-statistics.service';
|
||||
import { Telemetry } from '@/telemetry';
|
||||
import { MessageEventBus } from './eventbus/MessageEventBus/MessageEventBus';
|
||||
|
@ -39,16 +36,6 @@ export class InternalHooks {
|
|||
this.telemetry.track('Session started', { session_id: pushRef });
|
||||
}
|
||||
|
||||
onPersonalizationSurveySubmitted(userId: string, answers: Record<string, string>): void {
|
||||
const camelCaseKeys = Object.keys(answers);
|
||||
const personalizationSurveyData = { user_id: userId } as Record<string, string | string[]>;
|
||||
camelCaseKeys.forEach((camelCaseKey) => {
|
||||
personalizationSurveyData[snakeCase(camelCaseKey)] = answers[camelCaseKey];
|
||||
});
|
||||
|
||||
this.telemetry.track('User responded to personalization questions', personalizationSurveyData);
|
||||
}
|
||||
|
||||
onWorkflowSharingUpdate(workflowId: string, userId: string, userList: string[]) {
|
||||
const properties: ITelemetryTrackProperties = {
|
||||
workflow_id: workflowId,
|
||||
|
@ -67,76 +54,6 @@ export class InternalHooks {
|
|||
return await Promise.race([timeoutPromise, this.telemetry.trackN8nStop()]);
|
||||
}
|
||||
|
||||
onUserDeletion(userDeletionData: {
|
||||
user: User;
|
||||
telemetryData: ITelemetryUserDeletionData;
|
||||
publicApi: boolean;
|
||||
}) {
|
||||
this.telemetry.track('User deleted user', {
|
||||
...userDeletionData.telemetryData,
|
||||
user_id: userDeletionData.user.id,
|
||||
public_api: userDeletionData.publicApi,
|
||||
});
|
||||
}
|
||||
|
||||
onUserInvite(userInviteData: {
|
||||
user: User;
|
||||
target_user_id: string[];
|
||||
public_api: boolean;
|
||||
email_sent: boolean;
|
||||
invitee_role: string;
|
||||
}) {
|
||||
this.telemetry.track('User invited new user', {
|
||||
user_id: userInviteData.user.id,
|
||||
target_user_id: userInviteData.target_user_id,
|
||||
public_api: userInviteData.public_api,
|
||||
email_sent: userInviteData.email_sent,
|
||||
invitee_role: userInviteData.invitee_role,
|
||||
});
|
||||
}
|
||||
|
||||
onUserRoleChange(userRoleChangeData: {
|
||||
user: User;
|
||||
target_user_id: string;
|
||||
public_api: boolean;
|
||||
target_user_new_role: string;
|
||||
}) {
|
||||
const { user, ...rest } = userRoleChangeData;
|
||||
|
||||
this.telemetry.track('User changed role', { user_id: user.id, ...rest });
|
||||
}
|
||||
|
||||
onUserRetrievedUser(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||
this.telemetry.track('User retrieved user', userRetrievedData);
|
||||
}
|
||||
|
||||
onUserRetrievedAllUsers(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||
this.telemetry.track('User retrieved all users', userRetrievedData);
|
||||
}
|
||||
|
||||
onUserRetrievedExecution(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||
this.telemetry.track('User retrieved execution', userRetrievedData);
|
||||
}
|
||||
|
||||
onUserRetrievedAllExecutions(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||
this.telemetry.track('User retrieved all executions', userRetrievedData);
|
||||
}
|
||||
|
||||
onUserRetrievedWorkflow(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||
this.telemetry.track('User retrieved workflow', userRetrievedData);
|
||||
}
|
||||
|
||||
onUserRetrievedAllWorkflows(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||
this.telemetry.track('User retrieved all workflows', userRetrievedData);
|
||||
}
|
||||
|
||||
onUserUpdate(userUpdateData: { user: User; fields_changed: string[] }) {
|
||||
this.telemetry.track('User changed personal settings', {
|
||||
user_id: userUpdateData.user.id,
|
||||
fields_changed: userUpdateData.fields_changed,
|
||||
});
|
||||
}
|
||||
|
||||
onUserInviteEmailClick(userInviteClickData: { inviter: User; invitee: User }) {
|
||||
this.telemetry.track('User clicked invite link from email', {
|
||||
user_id: userInviteClickData.invitee.id,
|
||||
|
@ -172,19 +89,6 @@ export class InternalHooks {
|
|||
this.telemetry.track('Owner finished instance setup', instanceOwnerSetupData);
|
||||
}
|
||||
|
||||
onUserSignup(
|
||||
user: User,
|
||||
userSignupData: {
|
||||
user_type: AuthProviderType;
|
||||
was_disabled_ldap_user: boolean;
|
||||
},
|
||||
) {
|
||||
this.telemetry.track('User signed up', {
|
||||
user_id: user.id,
|
||||
...userSignupData,
|
||||
});
|
||||
}
|
||||
|
||||
onEmailFailed(failedEmailData: {
|
||||
user: User;
|
||||
message_type:
|
||||
|
|
|
@ -7,7 +7,7 @@ import { validCursor } from '../../shared/middlewares/global.middleware';
|
|||
import type { ExecutionRequest } from '../../../types';
|
||||
import { getSharedWorkflowIds } from '../workflows/workflows.service';
|
||||
import { encodeNextCursor } from '../../shared/services/pagination.service';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { ExecutionRepository } from '@db/repositories/execution.repository';
|
||||
import { ConcurrencyControlService } from '@/concurrency/concurrency-control.service';
|
||||
|
||||
|
@ -78,9 +78,9 @@ export = {
|
|||
return res.status(404).json({ message: 'Not Found' });
|
||||
}
|
||||
|
||||
Container.get(InternalHooks).onUserRetrievedExecution({
|
||||
user_id: req.user.id,
|
||||
public_api: true,
|
||||
Container.get(EventService).emit('user-retrieved-execution', {
|
||||
userId: req.user.id,
|
||||
publicApi: true,
|
||||
});
|
||||
|
||||
return res.json(replaceCircularReferences(execution));
|
||||
|
@ -130,9 +130,9 @@ export = {
|
|||
const count =
|
||||
await Container.get(ExecutionRepository).getExecutionsCountForPublicApi(filters);
|
||||
|
||||
Container.get(InternalHooks).onUserRetrievedAllExecutions({
|
||||
user_id: req.user.id,
|
||||
public_api: true,
|
||||
Container.get(EventService).emit('user-retrieved-all-executions', {
|
||||
userId: req.user.id,
|
||||
publicApi: true,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
validLicenseWithUserQuota,
|
||||
} from '../../shared/middlewares/global.middleware';
|
||||
import type { UserRequest } from '@/requests';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository';
|
||||
import type { Response } from 'express';
|
||||
import { InvitationController } from '@/controllers/invitation.controller';
|
||||
|
@ -37,12 +37,10 @@ export = {
|
|||
});
|
||||
}
|
||||
|
||||
const telemetryData = {
|
||||
user_id: req.user.id,
|
||||
public_api: true,
|
||||
};
|
||||
|
||||
Container.get(InternalHooks).onUserRetrievedUser(telemetryData);
|
||||
Container.get(EventService).emit('user-retrieved-user', {
|
||||
userId: req.user.id,
|
||||
publicApi: true,
|
||||
});
|
||||
|
||||
return res.json(clean(user, { includeRole }));
|
||||
},
|
||||
|
@ -65,12 +63,10 @@ export = {
|
|||
in: _in,
|
||||
});
|
||||
|
||||
const telemetryData = {
|
||||
user_id: req.user.id,
|
||||
public_api: true,
|
||||
};
|
||||
|
||||
Container.get(InternalHooks).onUserRetrievedAllUsers(telemetryData);
|
||||
Container.get(EventService).emit('user-retrieved-all-users', {
|
||||
userId: req.user.id,
|
||||
publicApi: true,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
data: clean(users, { includeRole }),
|
||||
|
|
|
@ -26,7 +26,6 @@ import {
|
|||
updateTags,
|
||||
} from './workflows.service';
|
||||
import { WorkflowService } from '@/workflows/workflow.service';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import { WorkflowHistoryService } from '@/workflows/workflowHistory/workflowHistory.service.ee';
|
||||
import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository';
|
||||
import { TagRepository } from '@/databases/repositories/tag.repository';
|
||||
|
@ -119,9 +118,9 @@ export = {
|
|||
return res.status(404).json({ message: 'Not Found' });
|
||||
}
|
||||
|
||||
Container.get(InternalHooks).onUserRetrievedWorkflow({
|
||||
user_id: req.user.id,
|
||||
public_api: true,
|
||||
Container.get(EventService).emit('user-retrieved-workflow', {
|
||||
userId: req.user.id,
|
||||
publicApi: true,
|
||||
});
|
||||
|
||||
return res.json(workflow);
|
||||
|
@ -185,9 +184,9 @@ export = {
|
|||
...(!config.getEnv('workflowTagsDisabled') && { relations: ['tags'] }),
|
||||
});
|
||||
|
||||
Container.get(InternalHooks).onUserRetrievedAllWorkflows({
|
||||
user_id: req.user.id,
|
||||
public_api: true,
|
||||
Container.get(EventService).emit('user-retrieved-all-workflows', {
|
||||
userId: req.user.id,
|
||||
publicApi: true,
|
||||
});
|
||||
|
||||
return res.json({
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Container } from 'typedi';
|
||||
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import { LdapService } from '@/Ldap/ldap.service.ee';
|
||||
import {
|
||||
createLdapUserOnLocalDb,
|
||||
|
@ -51,11 +50,11 @@ export const handleLdapLogin = async (
|
|||
await updateLdapUserOnLocalDb(identity, ldapAttributesValues);
|
||||
} else {
|
||||
const user = await createLdapUserOnLocalDb(ldapAttributesValues, ldapId);
|
||||
Container.get(InternalHooks).onUserSignup(user, {
|
||||
user_type: 'ldap',
|
||||
was_disabled_ldap_user: false,
|
||||
Container.get(EventService).emit('user-signed-up', {
|
||||
user,
|
||||
userType: 'ldap',
|
||||
wasDisabledLdapUser: false,
|
||||
});
|
||||
Container.get(EventService).emit('user-signed-up', { user });
|
||||
return user;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -13,6 +13,7 @@ import { InternalHooks } from '@/InternalHooks';
|
|||
import { License } from '@/License';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { UserRepository } from '@/databases/repositories/user.repository';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { badPasswords } from '@test/testData';
|
||||
import { mockInstance } from '@test/mocking';
|
||||
|
||||
|
@ -20,7 +21,8 @@ const browserId = 'test-browser-id';
|
|||
|
||||
describe('MeController', () => {
|
||||
const externalHooks = mockInstance(ExternalHooks);
|
||||
const internalHooks = mockInstance(InternalHooks);
|
||||
mockInstance(InternalHooks);
|
||||
const eventService = mockInstance(EventService);
|
||||
const userService = mockInstance(UserService);
|
||||
const userRepository = mockInstance(UserRepository);
|
||||
mockInstance(License).isWithinUsersLimit.mockReturnValue(true);
|
||||
|
@ -202,9 +204,9 @@ describe('MeController', () => {
|
|||
req.user.password,
|
||||
]);
|
||||
|
||||
expect(internalHooks.onUserUpdate).toHaveBeenCalledWith({
|
||||
expect(eventService.emit).toHaveBeenCalledWith('user-updated', {
|
||||
user: req.user,
|
||||
fields_changed: ['password'],
|
||||
fieldsChanged: ['password'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,7 +16,6 @@ import type { User } from '@/databases/entities/User';
|
|||
import { UserRepository } from '@db/repositories/user.repository';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import { ExternalHooks } from '@/ExternalHooks';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
|
@ -24,7 +23,6 @@ import { EventService } from '@/events/event.service';
|
|||
export class InvitationController {
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly internalHooks: InternalHooks,
|
||||
private readonly externalHooks: ExternalHooks,
|
||||
private readonly authService: AuthService,
|
||||
private readonly userService: UserService,
|
||||
|
@ -168,11 +166,11 @@ export class InvitationController {
|
|||
|
||||
this.authService.issueCookie(res, updatedUser, req.browserId);
|
||||
|
||||
this.internalHooks.onUserSignup(updatedUser, {
|
||||
user_type: 'email',
|
||||
was_disabled_ldap_user: false,
|
||||
this.eventService.emit('user-signed-up', {
|
||||
user: updatedUser,
|
||||
userType: 'email',
|
||||
wasDisabledLdapUser: false,
|
||||
});
|
||||
this.eventService.emit('user-signed-up', { user: updatedUser });
|
||||
|
||||
const publicInvitee = await this.userService.toPublic(invitee);
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ import { isSamlLicensedAndEnabled } from '@/sso/saml/samlHelpers';
|
|||
import { UserService } from '@/services/user.service';
|
||||
import { Logger } from '@/Logger';
|
||||
import { ExternalHooks } from '@/ExternalHooks';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { UserRepository } from '@/databases/repositories/user.repository';
|
||||
import { isApiEnabled } from '@/PublicApi';
|
||||
|
@ -40,7 +39,6 @@ export class MeController {
|
|||
constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly externalHooks: ExternalHooks,
|
||||
private readonly internalHooks: InternalHooks,
|
||||
private readonly authService: AuthService,
|
||||
private readonly userService: UserService,
|
||||
private readonly passwordUtility: PasswordUtility,
|
||||
|
@ -101,7 +99,6 @@ export class MeController {
|
|||
this.authService.issueCookie(res, user, req.browserId);
|
||||
|
||||
const fieldsChanged = Object.keys(payload);
|
||||
this.internalHooks.onUserUpdate({ user, fields_changed: fieldsChanged });
|
||||
this.eventService.emit('user-updated', { user, fieldsChanged });
|
||||
|
||||
const publicUser = await this.userService.toPublic(user);
|
||||
|
@ -151,7 +148,6 @@ export class MeController {
|
|||
|
||||
this.authService.issueCookie(res, updatedUser, req.browserId);
|
||||
|
||||
this.internalHooks.onUserUpdate({ user: updatedUser, fields_changed: ['password'] });
|
||||
this.eventService.emit('user-updated', { user: updatedUser, fieldsChanged: ['password'] });
|
||||
|
||||
await this.externalHooks.run('user.password.update', [updatedUser.email, updatedUser.password]);
|
||||
|
@ -186,7 +182,10 @@ export class MeController {
|
|||
|
||||
this.logger.info('User survey updated successfully', { userId: req.user.id });
|
||||
|
||||
this.internalHooks.onPersonalizationSurveySubmitted(req.user.id, personalizationAnswers);
|
||||
this.eventService.emit('user-submitted-personalization-survey', {
|
||||
userId: req.user.id,
|
||||
answers: personalizationAnswers,
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
|
|
@ -215,17 +215,16 @@ export class PasswordResetController {
|
|||
|
||||
this.authService.issueCookie(res, user, req.browserId);
|
||||
|
||||
this.internalHooks.onUserUpdate({ user, fields_changed: ['password'] });
|
||||
this.eventService.emit('user-updated', { user, fieldsChanged: ['password'] });
|
||||
|
||||
// if this user used to be an LDAP users
|
||||
// if this user used to be an LDAP user
|
||||
const ldapIdentity = user?.authIdentities?.find((i) => i.providerType === 'ldap');
|
||||
if (ldapIdentity) {
|
||||
this.internalHooks.onUserSignup(user, {
|
||||
user_type: 'email',
|
||||
was_disabled_ldap_user: true,
|
||||
this.eventService.emit('user-signed-up', {
|
||||
user,
|
||||
userType: 'email',
|
||||
wasDisabledLdapUser: true,
|
||||
});
|
||||
this.eventService.emit('user-signed-up', { user });
|
||||
}
|
||||
|
||||
await this.externalHooks.run('user.password.update', [user.email, passwordHash]);
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
UserRoleChangePayload,
|
||||
UserSettingsUpdatePayload,
|
||||
} from '@/requests';
|
||||
import type { PublicUser, ITelemetryUserDeletionData } from '@/Interfaces';
|
||||
import type { PublicUser } from '@/Interfaces';
|
||||
import { AuthIdentity } from '@db/entities/AuthIdentity';
|
||||
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
||||
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
||||
|
@ -21,7 +21,6 @@ import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
|
|||
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { ExternalHooks } from '@/ExternalHooks';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import { validateEntity } from '@/GenericHelpers';
|
||||
import { ProjectRepository } from '@/databases/repositories/project.repository';
|
||||
import { Project } from '@/databases/entities/Project';
|
||||
|
@ -35,7 +34,6 @@ export class UsersController {
|
|||
constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly externalHooks: ExternalHooks,
|
||||
private readonly internalHooks: InternalHooks,
|
||||
private readonly sharedCredentialsRepository: SharedCredentialsRepository,
|
||||
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
|
||||
private readonly userRepository: UserRepository,
|
||||
|
@ -183,12 +181,7 @@ export class UsersController {
|
|||
);
|
||||
}
|
||||
|
||||
const telemetryData: ITelemetryUserDeletionData = {
|
||||
user_id: req.user.id,
|
||||
target_user_old_status: userToDelete.isPending ? 'invited' : 'active',
|
||||
target_user_id: idToDelete,
|
||||
migration_strategy: transferId ? 'transfer_data' : 'delete_data',
|
||||
};
|
||||
let transfereeId;
|
||||
|
||||
if (transferId) {
|
||||
const transfereePersonalProject = await this.projectRepository.findOneBy({ id: transferId });
|
||||
|
@ -206,7 +199,7 @@ export class UsersController {
|
|||
},
|
||||
});
|
||||
|
||||
telemetryData.migration_user_id = transferee.id;
|
||||
transfereeId = transferee.id;
|
||||
|
||||
await this.userService.getManager().transaction(async (trx) => {
|
||||
await this.workflowService.transferAll(
|
||||
|
@ -253,12 +246,14 @@ export class UsersController {
|
|||
await trx.delete(User, { id: userToDelete.id });
|
||||
});
|
||||
|
||||
this.internalHooks.onUserDeletion({
|
||||
this.eventService.emit('user-deleted', {
|
||||
user: req.user,
|
||||
telemetryData,
|
||||
publicApi: false,
|
||||
targetUserOldStatus: userToDelete.isPending ? 'invited' : 'active',
|
||||
targetUserId: idToDelete,
|
||||
migrationStrategy: transferId ? 'transfer_data' : 'delete_data',
|
||||
migrationUserId: transfereeId,
|
||||
});
|
||||
this.eventService.emit('user-deleted', { user: req.user });
|
||||
|
||||
await this.externalHooks.run('user.deleted', [await this.userService.toPublic(userToDelete)]);
|
||||
|
||||
|
@ -294,11 +289,11 @@ export class UsersController {
|
|||
|
||||
await this.userService.update(targetUser.id, { role: payload.newRoleName });
|
||||
|
||||
this.internalHooks.onUserRoleChange({
|
||||
user: req.user,
|
||||
target_user_id: targetUser.id,
|
||||
target_user_new_role: ['global', payload.newRoleName].join(' '),
|
||||
public_api: false,
|
||||
this.eventService.emit('user-changed-role', {
|
||||
userId: req.user.id,
|
||||
targetUserId: targetUser.id,
|
||||
targetUserNewRole: ['global', payload.newRoleName].join(' '),
|
||||
publicApi: false,
|
||||
});
|
||||
|
||||
const projects = await this.projectService.getUserOwnedOrAdminProjects(targetUser.id);
|
||||
|
|
|
@ -242,6 +242,11 @@ describe('LogStreamingEventRelay', () => {
|
|||
lastName: 'Doe',
|
||||
role: 'some-role',
|
||||
},
|
||||
targetUserOldStatus: 'active',
|
||||
publicApi: false,
|
||||
migrationStrategy: 'transfer_data',
|
||||
targetUserId: '456',
|
||||
migrationUserId: '789',
|
||||
};
|
||||
|
||||
eventService.emit('user-deleted', event);
|
||||
|
|
|
@ -2,6 +2,7 @@ import type { AuthenticationMethod, IRun, IWorkflowBase } from 'n8n-workflow';
|
|||
import type { IWorkflowDb, IWorkflowExecutionDataProcess } from '@/Interfaces';
|
||||
import type { ProjectRole } from '@/databases/entities/ProjectRelation';
|
||||
import type { GlobalRole } from '@/databases/entities/User';
|
||||
import type { AuthProviderType } from '@/databases/entities/AuthIdentity';
|
||||
|
||||
export type UserLike = {
|
||||
id: string;
|
||||
|
@ -72,13 +73,26 @@ export type RelayEventMap = {
|
|||
|
||||
// #region User
|
||||
|
||||
'user-submitted-personalization-survey': {
|
||||
userId: string;
|
||||
answers: Record<string, string>;
|
||||
};
|
||||
|
||||
'user-deleted': {
|
||||
user: UserLike;
|
||||
publicApi: boolean;
|
||||
targetUserOldStatus: 'active' | 'invited';
|
||||
migrationStrategy?: 'transfer_data' | 'delete_data';
|
||||
targetUserId?: string;
|
||||
migrationUserId?: string;
|
||||
};
|
||||
|
||||
'user-invited': {
|
||||
user: UserLike;
|
||||
targetUserId: string[];
|
||||
publicApi: boolean;
|
||||
emailSent: boolean;
|
||||
inviteeRole: string;
|
||||
};
|
||||
|
||||
'user-reinvited': {
|
||||
|
@ -93,6 +107,8 @@ export type RelayEventMap = {
|
|||
|
||||
'user-signed-up': {
|
||||
user: UserLike;
|
||||
userType: AuthProviderType;
|
||||
wasDisabledLdapUser: boolean;
|
||||
};
|
||||
|
||||
'user-logged-in': {
|
||||
|
@ -106,6 +122,43 @@ export type RelayEventMap = {
|
|||
reason?: string;
|
||||
};
|
||||
|
||||
'user-changed-role': {
|
||||
userId: string;
|
||||
targetUserId: string;
|
||||
publicApi: boolean;
|
||||
targetUserNewRole: string;
|
||||
};
|
||||
|
||||
'user-retrieved-user': {
|
||||
userId: string;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
'user-retrieved-all-users': {
|
||||
userId: string;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
'user-retrieved-execution': {
|
||||
userId: string;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
'user-retrieved-all-executions': {
|
||||
userId: string;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
'user-retrieved-workflow': {
|
||||
userId: string;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
'user-retrieved-all-workflows': {
|
||||
userId: string;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Click
|
||||
|
|
|
@ -17,6 +17,7 @@ import { ProjectRelationRepository } from '@/databases/repositories/projectRelat
|
|||
import type { IExecutionTrackProperties } from '@/Interfaces';
|
||||
import { determineFinalExecutionStatus } from '@/executionLifecycleHooks/shared/sharedHookFunctions';
|
||||
import { EventRelay } from './event-relay';
|
||||
import { snakeCase } from 'change-case';
|
||||
|
||||
@Service()
|
||||
export class TelemetryEventRelay extends EventRelay {
|
||||
|
@ -73,6 +74,19 @@ export class TelemetryEventRelay extends EventRelay {
|
|||
'workflow-saved': async (event) => await this.workflowSaved(event),
|
||||
'server-started': async () => await this.serverStarted(),
|
||||
'workflow-post-execute': async (event) => await this.workflowPostExecute(event),
|
||||
'user-changed-role': (event) => this.userChangedRole(event),
|
||||
'user-retrieved-user': (event) => this.userRetrievedUser(event),
|
||||
'user-retrieved-all-users': (event) => this.userRetrievedAllUsers(event),
|
||||
'user-retrieved-execution': (event) => this.userRetrievedExecution(event),
|
||||
'user-retrieved-all-executions': (event) => this.userRetrievedAllExecutions(event),
|
||||
'user-retrieved-workflow': (event) => this.userRetrievedWorkflow(event),
|
||||
'user-retrieved-all-workflows': (event) => this.userRetrievedAllWorkflows(event),
|
||||
'user-updated': (event) => this.userUpdated(event),
|
||||
'user-deleted': (event) => this.userDeleted(event),
|
||||
'user-invited': (event) => this.userInvited(event),
|
||||
'user-signed-up': (event) => this.userSignedUp(event),
|
||||
'user-submitted-personalization-survey': (event) =>
|
||||
this.userSubmittedPersonalizationSurvey(event),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -744,4 +758,132 @@ export class TelemetryEventRelay extends EventRelay {
|
|||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region User
|
||||
|
||||
private userChangedRole({
|
||||
userId,
|
||||
targetUserId,
|
||||
targetUserNewRole,
|
||||
publicApi,
|
||||
}: RelayEventMap['user-changed-role']) {
|
||||
this.telemetry.track('User changed role', {
|
||||
user_id: userId,
|
||||
target_user_id: targetUserId,
|
||||
target_user_new_role: targetUserNewRole,
|
||||
public_api: publicApi,
|
||||
});
|
||||
}
|
||||
|
||||
private userRetrievedUser({ userId, publicApi }: RelayEventMap['user-retrieved-user']) {
|
||||
this.telemetry.track('User retrieved user', {
|
||||
user_id: userId,
|
||||
public_api: publicApi,
|
||||
});
|
||||
}
|
||||
|
||||
private userRetrievedAllUsers({ userId, publicApi }: RelayEventMap['user-retrieved-all-users']) {
|
||||
this.telemetry.track('User retrieved all users', {
|
||||
user_id: userId,
|
||||
public_api: publicApi,
|
||||
});
|
||||
}
|
||||
|
||||
private userRetrievedExecution({ userId, publicApi }: RelayEventMap['user-retrieved-execution']) {
|
||||
this.telemetry.track('User retrieved execution', {
|
||||
user_id: userId,
|
||||
public_api: publicApi,
|
||||
});
|
||||
}
|
||||
|
||||
private userRetrievedAllExecutions({
|
||||
userId,
|
||||
publicApi,
|
||||
}: RelayEventMap['user-retrieved-all-executions']) {
|
||||
this.telemetry.track('User retrieved all executions', {
|
||||
user_id: userId,
|
||||
public_api: publicApi,
|
||||
});
|
||||
}
|
||||
|
||||
private userRetrievedWorkflow({ userId, publicApi }: RelayEventMap['user-retrieved-workflow']) {
|
||||
this.telemetry.track('User retrieved workflow', {
|
||||
user_id: userId,
|
||||
public_api: publicApi,
|
||||
});
|
||||
}
|
||||
|
||||
private userRetrievedAllWorkflows({
|
||||
userId,
|
||||
publicApi,
|
||||
}: RelayEventMap['user-retrieved-all-workflows']) {
|
||||
this.telemetry.track('User retrieved all workflows', {
|
||||
user_id: userId,
|
||||
public_api: publicApi,
|
||||
});
|
||||
}
|
||||
|
||||
private userUpdated({ user, fieldsChanged }: RelayEventMap['user-updated']) {
|
||||
this.telemetry.track('User changed personal settings', {
|
||||
user_id: user.id,
|
||||
fields_changed: fieldsChanged,
|
||||
});
|
||||
}
|
||||
|
||||
private userDeleted({
|
||||
user,
|
||||
publicApi,
|
||||
targetUserOldStatus,
|
||||
migrationStrategy,
|
||||
targetUserId,
|
||||
migrationUserId,
|
||||
}: RelayEventMap['user-deleted']) {
|
||||
this.telemetry.track('User deleted user', {
|
||||
user_id: user.id,
|
||||
public_api: publicApi,
|
||||
target_user_old_status: targetUserOldStatus,
|
||||
migration_strategy: migrationStrategy,
|
||||
target_user_id: targetUserId,
|
||||
migration_user_id: migrationUserId,
|
||||
});
|
||||
}
|
||||
|
||||
private userInvited({
|
||||
user,
|
||||
targetUserId,
|
||||
publicApi,
|
||||
emailSent,
|
||||
inviteeRole,
|
||||
}: RelayEventMap['user-invited']) {
|
||||
this.telemetry.track('User invited new user', {
|
||||
user_id: user.id,
|
||||
target_user_id: targetUserId,
|
||||
public_api: publicApi,
|
||||
email_sent: emailSent,
|
||||
invitee_role: inviteeRole,
|
||||
});
|
||||
}
|
||||
|
||||
private userSignedUp({ user, userType, wasDisabledLdapUser }: RelayEventMap['user-signed-up']) {
|
||||
this.telemetry.track('User signed up', {
|
||||
user_id: user.id,
|
||||
user_type: userType,
|
||||
was_disabled_ldap_user: wasDisabledLdapUser,
|
||||
});
|
||||
}
|
||||
|
||||
private userSubmittedPersonalizationSurvey({
|
||||
userId,
|
||||
answers,
|
||||
}: RelayEventMap['user-submitted-personalization-survey']) {
|
||||
const camelCaseKeys = Object.keys(answers);
|
||||
const personalizationSurveyData = { user_id: userId } as Record<string, string | string[]>;
|
||||
camelCaseKeys.forEach((camelCaseKey) => {
|
||||
personalizationSurveyData[snakeCase(camelCaseKey)] = answers[camelCaseKey];
|
||||
});
|
||||
|
||||
this.telemetry.track('User responded to personalization questions', personalizationSurveyData);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
}
|
||||
|
|
|
@ -151,16 +151,12 @@ export class UserService {
|
|||
});
|
||||
}
|
||||
|
||||
Container.get(InternalHooks).onUserInvite({
|
||||
user: owner,
|
||||
target_user_id: Object.values(toInviteUsers),
|
||||
public_api: false,
|
||||
email_sent: result.emailSent,
|
||||
invitee_role: role, // same role for all invited users
|
||||
});
|
||||
this.eventService.emit('user-invited', {
|
||||
user: owner,
|
||||
targetUserId: Object.values(toInviteUsers),
|
||||
publicApi: false,
|
||||
emailSent: result.emailSent,
|
||||
inviteeRole: role, // same role for all invited users
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
|
|
Loading…
Reference in a new issue