Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): Flush instance stopped event immediately #10238

Merged
merged 2 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 65 additions & 106 deletions packages/cli/src/InternalHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,49 +43,41 @@ export class InternalHooks {
private readonly projectRelationRepository: ProjectRelationRepository,
private readonly _eventBus: MessageEventBus, // needed until we decouple telemetry
) {
workflowStatisticsService.on(
'telemetry.onFirstProductionWorkflowSuccess',
async (metrics) => await this.onFirstProductionWorkflowSuccess(metrics),
workflowStatisticsService.on('telemetry.onFirstProductionWorkflowSuccess', (metrics) =>
this.onFirstProductionWorkflowSuccess(metrics),
);
workflowStatisticsService.on(
'telemetry.onFirstWorkflowDataLoad',
async (metrics) => await this.onFirstWorkflowDataLoad(metrics),
workflowStatisticsService.on('telemetry.onFirstWorkflowDataLoad', (metrics) =>
this.onFirstWorkflowDataLoad(metrics),
);
}

async init() {
await this.telemetry.init();
}

async onFrontendSettingsAPI(pushRef?: string): Promise<void> {
return await this.telemetry.track('Session started', { session_id: pushRef });
onFrontendSettingsAPI(pushRef?: string): void {
this.telemetry.track('Session started', { session_id: pushRef });
}

async onPersonalizationSurveySubmitted(
userId: string,
answers: Record<string, string>,
): Promise<void> {
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];
});

return await this.telemetry.track(
'User responded to personalization questions',
personalizationSurveyData,
);
this.telemetry.track('User responded to personalization questions', personalizationSurveyData);
}

async onWorkflowCreated(
onWorkflowCreated(
user: User,
workflow: IWorkflowBase,
project: Project,
publicApi: boolean,
): Promise<void> {
): void {
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);

void this.telemetry.track('User created workflow', {
this.telemetry.track('User created workflow', {
user_id: user.id,
workflow_id: workflow.id,
node_graph_string: JSON.stringify(nodeGraph),
Expand All @@ -95,8 +87,8 @@ export class InternalHooks {
});
}

async onWorkflowDeleted(user: User, workflowId: string, publicApi: boolean): Promise<void> {
void this.telemetry.track('User deleted workflow', {
onWorkflowDeleted(user: User, workflowId: string, publicApi: boolean): void {
this.telemetry.track('User deleted workflow', {
user_id: user.id,
workflow_id: workflowId,
public_api: publicApi,
Expand Down Expand Up @@ -136,7 +128,7 @@ export class InternalHooks {
(note) => note.overlapping,
).length;

void this.telemetry.track('User saved workflow', {
this.telemetry.track('User saved workflow', {
user_id: user.id,
workflow_id: workflow.id,
node_graph_string: JSON.stringify(nodeGraph),
Expand All @@ -155,7 +147,7 @@ export class InternalHooks {
workflow: IWorkflowBase,
runData?: IRun,
userId?: string,
): Promise<void> {
) {
if (!workflow.id) {
return;
}
Expand All @@ -165,8 +157,6 @@ export class InternalHooks {
return;
}

const promises = [];

const telemetryProperties: IExecutionTrackProperties = {
workflow_id: workflow.id,
is_manual: false,
Expand Down Expand Up @@ -270,7 +260,7 @@ export class InternalHooks {
node_id: nodeGraphResult.nameIndices[runData.data.startData?.destinationNode],
};

promises.push(this.telemetry.track('Manual node exec finished', telemetryPayload));
this.telemetry.track('Manual node exec finished', telemetryPayload);
} else {
nodeGraphResult.webhookNodeNames.forEach((name: string) => {
const execJson = runData.data.resultData.runData[name]?.[0]?.data?.main?.[0]?.[0]
Expand All @@ -282,56 +272,52 @@ export class InternalHooks {
}
});

promises.push(
this.telemetry.track('Manual workflow exec finished', manualExecEventProperties),
);
this.telemetry.track('Manual workflow exec finished', manualExecEventProperties);
}
}
}

void Promise.all([...promises, this.telemetry.trackWorkflowExecution(telemetryProperties)]);
this.telemetry.trackWorkflowExecution(telemetryProperties);
}

async onWorkflowSharingUpdate(workflowId: string, userId: string, userList: string[]) {
onWorkflowSharingUpdate(workflowId: string, userId: string, userList: string[]) {
const properties: ITelemetryTrackProperties = {
workflow_id: workflowId,
user_id_sharer: userId,
user_id_list: userList,
};

return await this.telemetry.track('User updated workflow sharing', properties);
this.telemetry.track('User updated workflow sharing', properties);
}

async onN8nStop(): Promise<void> {
const timeoutPromise = new Promise<void>((resolve) => {
setTimeout(() => {
resolve();
}, 3000);
setTimeout(resolve, 3000);
});

return await Promise.race([timeoutPromise, this.telemetry.trackN8nStop()]);
}

async onUserDeletion(userDeletionData: {
onUserDeletion(userDeletionData: {
user: User;
telemetryData: ITelemetryUserDeletionData;
publicApi: boolean;
}): Promise<void> {
void this.telemetry.track('User deleted user', {
}) {
this.telemetry.track('User deleted user', {
...userDeletionData.telemetryData,
user_id: userDeletionData.user.id,
public_api: userDeletionData.publicApi,
});
}

async onUserInvite(userInviteData: {
onUserInvite(userInviteData: {
user: User;
target_user_id: string[];
public_api: boolean;
email_sent: boolean;
invitee_role: string;
}): Promise<void> {
void this.telemetry.track('User invited new user', {
}) {
this.telemetry.track('User invited new user', {
user_id: userInviteData.user.id,
target_user_id: userInviteData.target_user_id,
public_api: userInviteData.public_api,
Expand All @@ -340,82 +326,61 @@ export class InternalHooks {
});
}

async onUserRoleChange(userRoleChangeData: {
onUserRoleChange(userRoleChangeData: {
user: User;
target_user_id: string;
public_api: boolean;
target_user_new_role: string;
}) {
const { user, ...rest } = userRoleChangeData;

void this.telemetry.track('User changed role', { user_id: user.id, ...rest });
this.telemetry.track('User changed role', { user_id: user.id, ...rest });
}

async onUserRetrievedUser(userRetrievedData: {
user_id: string;
public_api: boolean;
}): Promise<void> {
return await this.telemetry.track('User retrieved user', userRetrievedData);
onUserRetrievedUser(userRetrievedData: { user_id: string; public_api: boolean }) {
this.telemetry.track('User retrieved user', userRetrievedData);
}

async onUserRetrievedAllUsers(userRetrievedData: {
user_id: string;
public_api: boolean;
}): Promise<void> {
return await this.telemetry.track('User retrieved all users', userRetrievedData);
onUserRetrievedAllUsers(userRetrievedData: { user_id: string; public_api: boolean }) {
this.telemetry.track('User retrieved all users', userRetrievedData);
}

async onUserRetrievedExecution(userRetrievedData: {
user_id: string;
public_api: boolean;
}): Promise<void> {
return await this.telemetry.track('User retrieved execution', userRetrievedData);
onUserRetrievedExecution(userRetrievedData: { user_id: string; public_api: boolean }) {
this.telemetry.track('User retrieved execution', userRetrievedData);
}

async onUserRetrievedAllExecutions(userRetrievedData: {
user_id: string;
public_api: boolean;
}): Promise<void> {
return await this.telemetry.track('User retrieved all executions', userRetrievedData);
onUserRetrievedAllExecutions(userRetrievedData: { user_id: string; public_api: boolean }) {
this.telemetry.track('User retrieved all executions', userRetrievedData);
}

async onUserRetrievedWorkflow(userRetrievedData: {
user_id: string;
public_api: boolean;
}): Promise<void> {
return await this.telemetry.track('User retrieved workflow', userRetrievedData);
onUserRetrievedWorkflow(userRetrievedData: { user_id: string; public_api: boolean }) {
this.telemetry.track('User retrieved workflow', userRetrievedData);
}

async onUserRetrievedAllWorkflows(userRetrievedData: {
user_id: string;
public_api: boolean;
}): Promise<void> {
return await this.telemetry.track('User retrieved all workflows', userRetrievedData);
onUserRetrievedAllWorkflows(userRetrievedData: { user_id: string; public_api: boolean }) {
this.telemetry.track('User retrieved all workflows', userRetrievedData);
}

async onUserUpdate(userUpdateData: { user: User; fields_changed: string[] }): Promise<void> {
void this.telemetry.track('User changed personal settings', {
onUserUpdate(userUpdateData: { user: User; fields_changed: string[] }) {
this.telemetry.track('User changed personal settings', {
user_id: userUpdateData.user.id,
fields_changed: userUpdateData.fields_changed,
});
}

async onUserInviteEmailClick(userInviteClickData: {
inviter: User;
invitee: User;
}): Promise<void> {
void this.telemetry.track('User clicked invite link from email', {
onUserInviteEmailClick(userInviteClickData: { inviter: User; invitee: User }) {
this.telemetry.track('User clicked invite link from email', {
user_id: userInviteClickData.invitee.id,
});
}

async onUserPasswordResetEmailClick(userPasswordResetData: { user: User }): Promise<void> {
void this.telemetry.track('User clicked password reset link from email', {
onUserPasswordResetEmailClick(userPasswordResetData: { user: User }) {
this.telemetry.track('User clicked password reset link from email', {
user_id: userPasswordResetData.user.id,
});
}

async onUserTransactionalEmail(userTransactionalEmailData: {
onUserTransactionalEmail(userTransactionalEmailData: {
user_id: string;
message_type:
| 'Reset password'
Expand All @@ -424,37 +389,34 @@ export class InternalHooks {
| 'Workflow shared'
| 'Credentials shared';
public_api: boolean;
}): Promise<void> {
return await this.telemetry.track(
'Instance sent transactional email to user',
userTransactionalEmailData,
);
}) {
this.telemetry.track('Instance sent transactional email to user', userTransactionalEmailData);
}

async onUserPasswordResetRequestClick(userPasswordResetData: { user: User }): Promise<void> {
void this.telemetry.track('User requested password reset while logged out', {
onUserPasswordResetRequestClick(userPasswordResetData: { user: User }) {
this.telemetry.track('User requested password reset while logged out', {
user_id: userPasswordResetData.user.id,
});
}

async onInstanceOwnerSetup(instanceOwnerSetupData: { user_id: string }): Promise<void> {
return await this.telemetry.track('Owner finished instance setup', instanceOwnerSetupData);
onInstanceOwnerSetup(instanceOwnerSetupData: { user_id: string }) {
this.telemetry.track('Owner finished instance setup', instanceOwnerSetupData);
}

async onUserSignup(
onUserSignup(
user: User,
userSignupData: {
user_type: AuthProviderType;
was_disabled_ldap_user: boolean;
},
): Promise<void> {
void this.telemetry.track('User signed up', {
) {
this.telemetry.track('User signed up', {
user_id: user.id,
...userSignupData,
});
}

async onEmailFailed(failedEmailData: {
onEmailFailed(failedEmailData: {
user: User;
message_type:
| 'Reset password'
Expand All @@ -463,30 +425,27 @@ export class InternalHooks {
| 'Workflow shared'
| 'Credentials shared';
public_api: boolean;
}): Promise<void> {
void this.telemetry.track('Instance failed to send transactional email to user', {
}) {
this.telemetry.track('Instance failed to send transactional email to user', {
user_id: failedEmailData.user.id,
});
}

/*
* Execution Statistics
*/
async onFirstProductionWorkflowSuccess(data: {
user_id: string;
workflow_id: string;
}): Promise<void> {
return await this.telemetry.track('Workflow first prod success', data);
onFirstProductionWorkflowSuccess(data: { user_id: string; workflow_id: string }) {
this.telemetry.track('Workflow first prod success', data);
}

async onFirstWorkflowDataLoad(data: {
onFirstWorkflowDataLoad(data: {
user_id: string;
workflow_id: string;
node_type: string;
node_id: string;
credential_type?: string;
credential_id?: string;
}): Promise<void> {
return await this.telemetry.track('Workflow first data fetched', data);
}) {
this.telemetry.track('Workflow first data fetched', data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export = {
return res.status(404).json({ message: 'Not Found' });
}

void Container.get(InternalHooks).onUserRetrievedExecution({
Container.get(InternalHooks).onUserRetrievedExecution({
user_id: req.user.id,
public_api: true,
});
Expand Down Expand Up @@ -129,7 +129,7 @@ export = {
const count =
await Container.get(ExecutionRepository).getExecutionsCountForPublicApi(filters);

void Container.get(InternalHooks).onUserRetrievedAllExecutions({
Container.get(InternalHooks).onUserRetrievedAllExecutions({
user_id: req.user.id,
public_api: true,
});
Expand Down
Loading
Loading