mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(core): Flush instance stopped event immediately (#10238)
This commit is contained in:
parent
a2d08846d0
commit
d6770b5fca
|
@ -43,13 +43,11 @@ export class InternalHooks {
|
||||||
private readonly projectRelationRepository: ProjectRelationRepository,
|
private readonly projectRelationRepository: ProjectRelationRepository,
|
||||||
private readonly _eventBus: MessageEventBus, // needed until we decouple telemetry
|
private readonly _eventBus: MessageEventBus, // needed until we decouple telemetry
|
||||||
) {
|
) {
|
||||||
workflowStatisticsService.on(
|
workflowStatisticsService.on('telemetry.onFirstProductionWorkflowSuccess', (metrics) =>
|
||||||
'telemetry.onFirstProductionWorkflowSuccess',
|
this.onFirstProductionWorkflowSuccess(metrics),
|
||||||
async (metrics) => await this.onFirstProductionWorkflowSuccess(metrics),
|
|
||||||
);
|
);
|
||||||
workflowStatisticsService.on(
|
workflowStatisticsService.on('telemetry.onFirstWorkflowDataLoad', (metrics) =>
|
||||||
'telemetry.onFirstWorkflowDataLoad',
|
this.onFirstWorkflowDataLoad(metrics),
|
||||||
async (metrics) => await this.onFirstWorkflowDataLoad(metrics),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,35 +55,29 @@ export class InternalHooks {
|
||||||
await this.telemetry.init();
|
await this.telemetry.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
async onFrontendSettingsAPI(pushRef?: string): Promise<void> {
|
onFrontendSettingsAPI(pushRef?: string): void {
|
||||||
return await this.telemetry.track('Session started', { session_id: pushRef });
|
this.telemetry.track('Session started', { session_id: pushRef });
|
||||||
}
|
}
|
||||||
|
|
||||||
async onPersonalizationSurveySubmitted(
|
onPersonalizationSurveySubmitted(userId: string, answers: Record<string, string>): void {
|
||||||
userId: string,
|
|
||||||
answers: Record<string, string>,
|
|
||||||
): Promise<void> {
|
|
||||||
const camelCaseKeys = Object.keys(answers);
|
const camelCaseKeys = Object.keys(answers);
|
||||||
const personalizationSurveyData = { user_id: userId } as Record<string, string | string[]>;
|
const personalizationSurveyData = { user_id: userId } as Record<string, string | string[]>;
|
||||||
camelCaseKeys.forEach((camelCaseKey) => {
|
camelCaseKeys.forEach((camelCaseKey) => {
|
||||||
personalizationSurveyData[snakeCase(camelCaseKey)] = answers[camelCaseKey];
|
personalizationSurveyData[snakeCase(camelCaseKey)] = answers[camelCaseKey];
|
||||||
});
|
});
|
||||||
|
|
||||||
return await this.telemetry.track(
|
this.telemetry.track('User responded to personalization questions', personalizationSurveyData);
|
||||||
'User responded to personalization questions',
|
|
||||||
personalizationSurveyData,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onWorkflowCreated(
|
onWorkflowCreated(
|
||||||
user: User,
|
user: User,
|
||||||
workflow: IWorkflowBase,
|
workflow: IWorkflowBase,
|
||||||
project: Project,
|
project: Project,
|
||||||
publicApi: boolean,
|
publicApi: boolean,
|
||||||
): Promise<void> {
|
): void {
|
||||||
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
|
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
|
||||||
|
|
||||||
void this.telemetry.track('User created workflow', {
|
this.telemetry.track('User created workflow', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
workflow_id: workflow.id,
|
workflow_id: workflow.id,
|
||||||
node_graph_string: JSON.stringify(nodeGraph),
|
node_graph_string: JSON.stringify(nodeGraph),
|
||||||
|
@ -95,8 +87,8 @@ export class InternalHooks {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onWorkflowDeleted(user: User, workflowId: string, publicApi: boolean): Promise<void> {
|
onWorkflowDeleted(user: User, workflowId: string, publicApi: boolean): void {
|
||||||
void this.telemetry.track('User deleted workflow', {
|
this.telemetry.track('User deleted workflow', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
workflow_id: workflowId,
|
workflow_id: workflowId,
|
||||||
public_api: publicApi,
|
public_api: publicApi,
|
||||||
|
@ -136,7 +128,7 @@ export class InternalHooks {
|
||||||
(note) => note.overlapping,
|
(note) => note.overlapping,
|
||||||
).length;
|
).length;
|
||||||
|
|
||||||
void this.telemetry.track('User saved workflow', {
|
this.telemetry.track('User saved workflow', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
workflow_id: workflow.id,
|
workflow_id: workflow.id,
|
||||||
node_graph_string: JSON.stringify(nodeGraph),
|
node_graph_string: JSON.stringify(nodeGraph),
|
||||||
|
@ -155,7 +147,7 @@ export class InternalHooks {
|
||||||
workflow: IWorkflowBase,
|
workflow: IWorkflowBase,
|
||||||
runData?: IRun,
|
runData?: IRun,
|
||||||
userId?: string,
|
userId?: string,
|
||||||
): Promise<void> {
|
) {
|
||||||
if (!workflow.id) {
|
if (!workflow.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -165,8 +157,6 @@ export class InternalHooks {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const promises = [];
|
|
||||||
|
|
||||||
const telemetryProperties: IExecutionTrackProperties = {
|
const telemetryProperties: IExecutionTrackProperties = {
|
||||||
workflow_id: workflow.id,
|
workflow_id: workflow.id,
|
||||||
is_manual: false,
|
is_manual: false,
|
||||||
|
@ -270,7 +260,7 @@ export class InternalHooks {
|
||||||
node_id: nodeGraphResult.nameIndices[runData.data.startData?.destinationNode],
|
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 {
|
} else {
|
||||||
nodeGraphResult.webhookNodeNames.forEach((name: string) => {
|
nodeGraphResult.webhookNodeNames.forEach((name: string) => {
|
||||||
const execJson = runData.data.resultData.runData[name]?.[0]?.data?.main?.[0]?.[0]
|
const execJson = runData.data.resultData.runData[name]?.[0]?.data?.main?.[0]?.[0]
|
||||||
|
@ -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 = {
|
const properties: ITelemetryTrackProperties = {
|
||||||
workflow_id: workflowId,
|
workflow_id: workflowId,
|
||||||
user_id_sharer: userId,
|
user_id_sharer: userId,
|
||||||
user_id_list: userList,
|
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> {
|
async onN8nStop(): Promise<void> {
|
||||||
const timeoutPromise = new Promise<void>((resolve) => {
|
const timeoutPromise = new Promise<void>((resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(resolve, 3000);
|
||||||
resolve();
|
|
||||||
}, 3000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return await Promise.race([timeoutPromise, this.telemetry.trackN8nStop()]);
|
return await Promise.race([timeoutPromise, this.telemetry.trackN8nStop()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserDeletion(userDeletionData: {
|
onUserDeletion(userDeletionData: {
|
||||||
user: User;
|
user: User;
|
||||||
telemetryData: ITelemetryUserDeletionData;
|
telemetryData: ITelemetryUserDeletionData;
|
||||||
publicApi: boolean;
|
publicApi: boolean;
|
||||||
}): Promise<void> {
|
}) {
|
||||||
void this.telemetry.track('User deleted user', {
|
this.telemetry.track('User deleted user', {
|
||||||
...userDeletionData.telemetryData,
|
...userDeletionData.telemetryData,
|
||||||
user_id: userDeletionData.user.id,
|
user_id: userDeletionData.user.id,
|
||||||
public_api: userDeletionData.publicApi,
|
public_api: userDeletionData.publicApi,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserInvite(userInviteData: {
|
onUserInvite(userInviteData: {
|
||||||
user: User;
|
user: User;
|
||||||
target_user_id: string[];
|
target_user_id: string[];
|
||||||
public_api: boolean;
|
public_api: boolean;
|
||||||
email_sent: boolean;
|
email_sent: boolean;
|
||||||
invitee_role: string;
|
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,
|
user_id: userInviteData.user.id,
|
||||||
target_user_id: userInviteData.target_user_id,
|
target_user_id: userInviteData.target_user_id,
|
||||||
public_api: userInviteData.public_api,
|
public_api: userInviteData.public_api,
|
||||||
|
@ -340,7 +326,7 @@ export class InternalHooks {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserRoleChange(userRoleChangeData: {
|
onUserRoleChange(userRoleChangeData: {
|
||||||
user: User;
|
user: User;
|
||||||
target_user_id: string;
|
target_user_id: string;
|
||||||
public_api: boolean;
|
public_api: boolean;
|
||||||
|
@ -348,74 +334,53 @@ export class InternalHooks {
|
||||||
}) {
|
}) {
|
||||||
const { user, ...rest } = userRoleChangeData;
|
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: {
|
onUserRetrievedUser(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||||
user_id: string;
|
this.telemetry.track('User retrieved user', userRetrievedData);
|
||||||
public_api: boolean;
|
|
||||||
}): Promise<void> {
|
|
||||||
return await this.telemetry.track('User retrieved user', userRetrievedData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserRetrievedAllUsers(userRetrievedData: {
|
onUserRetrievedAllUsers(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||||
user_id: string;
|
this.telemetry.track('User retrieved all users', userRetrievedData);
|
||||||
public_api: boolean;
|
|
||||||
}): Promise<void> {
|
|
||||||
return await this.telemetry.track('User retrieved all users', userRetrievedData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserRetrievedExecution(userRetrievedData: {
|
onUserRetrievedExecution(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||||
user_id: string;
|
this.telemetry.track('User retrieved execution', userRetrievedData);
|
||||||
public_api: boolean;
|
|
||||||
}): Promise<void> {
|
|
||||||
return await this.telemetry.track('User retrieved execution', userRetrievedData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserRetrievedAllExecutions(userRetrievedData: {
|
onUserRetrievedAllExecutions(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||||
user_id: string;
|
this.telemetry.track('User retrieved all executions', userRetrievedData);
|
||||||
public_api: boolean;
|
|
||||||
}): Promise<void> {
|
|
||||||
return await this.telemetry.track('User retrieved all executions', userRetrievedData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserRetrievedWorkflow(userRetrievedData: {
|
onUserRetrievedWorkflow(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||||
user_id: string;
|
this.telemetry.track('User retrieved workflow', userRetrievedData);
|
||||||
public_api: boolean;
|
|
||||||
}): Promise<void> {
|
|
||||||
return await this.telemetry.track('User retrieved workflow', userRetrievedData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserRetrievedAllWorkflows(userRetrievedData: {
|
onUserRetrievedAllWorkflows(userRetrievedData: { user_id: string; public_api: boolean }) {
|
||||||
user_id: string;
|
this.telemetry.track('User retrieved all workflows', userRetrievedData);
|
||||||
public_api: boolean;
|
|
||||||
}): Promise<void> {
|
|
||||||
return await this.telemetry.track('User retrieved all workflows', userRetrievedData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserUpdate(userUpdateData: { user: User; fields_changed: string[] }): Promise<void> {
|
onUserUpdate(userUpdateData: { user: User; fields_changed: string[] }) {
|
||||||
void this.telemetry.track('User changed personal settings', {
|
this.telemetry.track('User changed personal settings', {
|
||||||
user_id: userUpdateData.user.id,
|
user_id: userUpdateData.user.id,
|
||||||
fields_changed: userUpdateData.fields_changed,
|
fields_changed: userUpdateData.fields_changed,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserInviteEmailClick(userInviteClickData: {
|
onUserInviteEmailClick(userInviteClickData: { inviter: User; invitee: User }) {
|
||||||
inviter: User;
|
this.telemetry.track('User clicked invite link from email', {
|
||||||
invitee: User;
|
|
||||||
}): Promise<void> {
|
|
||||||
void this.telemetry.track('User clicked invite link from email', {
|
|
||||||
user_id: userInviteClickData.invitee.id,
|
user_id: userInviteClickData.invitee.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserPasswordResetEmailClick(userPasswordResetData: { user: User }): Promise<void> {
|
onUserPasswordResetEmailClick(userPasswordResetData: { user: User }) {
|
||||||
void this.telemetry.track('User clicked password reset link from email', {
|
this.telemetry.track('User clicked password reset link from email', {
|
||||||
user_id: userPasswordResetData.user.id,
|
user_id: userPasswordResetData.user.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserTransactionalEmail(userTransactionalEmailData: {
|
onUserTransactionalEmail(userTransactionalEmailData: {
|
||||||
user_id: string;
|
user_id: string;
|
||||||
message_type:
|
message_type:
|
||||||
| 'Reset password'
|
| 'Reset password'
|
||||||
|
@ -424,37 +389,34 @@ export class InternalHooks {
|
||||||
| 'Workflow shared'
|
| 'Workflow shared'
|
||||||
| 'Credentials shared';
|
| 'Credentials shared';
|
||||||
public_api: boolean;
|
public_api: boolean;
|
||||||
}): Promise<void> {
|
}) {
|
||||||
return await this.telemetry.track(
|
this.telemetry.track('Instance sent transactional email to user', userTransactionalEmailData);
|
||||||
'Instance sent transactional email to user',
|
|
||||||
userTransactionalEmailData,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserPasswordResetRequestClick(userPasswordResetData: { user: User }): Promise<void> {
|
onUserPasswordResetRequestClick(userPasswordResetData: { user: User }) {
|
||||||
void this.telemetry.track('User requested password reset while logged out', {
|
this.telemetry.track('User requested password reset while logged out', {
|
||||||
user_id: userPasswordResetData.user.id,
|
user_id: userPasswordResetData.user.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onInstanceOwnerSetup(instanceOwnerSetupData: { user_id: string }): Promise<void> {
|
onInstanceOwnerSetup(instanceOwnerSetupData: { user_id: string }) {
|
||||||
return await this.telemetry.track('Owner finished instance setup', instanceOwnerSetupData);
|
this.telemetry.track('Owner finished instance setup', instanceOwnerSetupData);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUserSignup(
|
onUserSignup(
|
||||||
user: User,
|
user: User,
|
||||||
userSignupData: {
|
userSignupData: {
|
||||||
user_type: AuthProviderType;
|
user_type: AuthProviderType;
|
||||||
was_disabled_ldap_user: boolean;
|
was_disabled_ldap_user: boolean;
|
||||||
},
|
},
|
||||||
): Promise<void> {
|
) {
|
||||||
void this.telemetry.track('User signed up', {
|
this.telemetry.track('User signed up', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
...userSignupData,
|
...userSignupData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onEmailFailed(failedEmailData: {
|
onEmailFailed(failedEmailData: {
|
||||||
user: User;
|
user: User;
|
||||||
message_type:
|
message_type:
|
||||||
| 'Reset password'
|
| 'Reset password'
|
||||||
|
@ -463,8 +425,8 @@ export class InternalHooks {
|
||||||
| 'Workflow shared'
|
| 'Workflow shared'
|
||||||
| 'Credentials shared';
|
| 'Credentials shared';
|
||||||
public_api: boolean;
|
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,
|
user_id: failedEmailData.user.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -472,21 +434,18 @@ export class InternalHooks {
|
||||||
/*
|
/*
|
||||||
* Execution Statistics
|
* Execution Statistics
|
||||||
*/
|
*/
|
||||||
async onFirstProductionWorkflowSuccess(data: {
|
onFirstProductionWorkflowSuccess(data: { user_id: string; workflow_id: string }) {
|
||||||
user_id: string;
|
this.telemetry.track('Workflow first prod success', data);
|
||||||
workflow_id: string;
|
|
||||||
}): Promise<void> {
|
|
||||||
return await this.telemetry.track('Workflow first prod success', data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onFirstWorkflowDataLoad(data: {
|
onFirstWorkflowDataLoad(data: {
|
||||||
user_id: string;
|
user_id: string;
|
||||||
workflow_id: string;
|
workflow_id: string;
|
||||||
node_type: string;
|
node_type: string;
|
||||||
node_id: string;
|
node_id: string;
|
||||||
credential_type?: string;
|
credential_type?: string;
|
||||||
credential_id?: string;
|
credential_id?: string;
|
||||||
}): Promise<void> {
|
}) {
|
||||||
return await this.telemetry.track('Workflow first data fetched', data);
|
this.telemetry.track('Workflow first data fetched', data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ export = {
|
||||||
return res.status(404).json({ message: 'Not Found' });
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container.get(InternalHooks).onUserRetrievedExecution({
|
Container.get(InternalHooks).onUserRetrievedExecution({
|
||||||
user_id: req.user.id,
|
user_id: req.user.id,
|
||||||
public_api: true,
|
public_api: true,
|
||||||
});
|
});
|
||||||
|
@ -129,7 +129,7 @@ export = {
|
||||||
const count =
|
const count =
|
||||||
await Container.get(ExecutionRepository).getExecutionsCountForPublicApi(filters);
|
await Container.get(ExecutionRepository).getExecutionsCountForPublicApi(filters);
|
||||||
|
|
||||||
void Container.get(InternalHooks).onUserRetrievedAllExecutions({
|
Container.get(InternalHooks).onUserRetrievedAllExecutions({
|
||||||
user_id: req.user.id,
|
user_id: req.user.id,
|
||||||
public_api: true,
|
public_api: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -33,7 +33,7 @@ export = {
|
||||||
public_api: true,
|
public_api: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
void Container.get(InternalHooks).onUserRetrievedUser(telemetryData);
|
Container.get(InternalHooks).onUserRetrievedUser(telemetryData);
|
||||||
|
|
||||||
return res.json(clean(user, { includeRole }));
|
return res.json(clean(user, { includeRole }));
|
||||||
},
|
},
|
||||||
|
@ -56,7 +56,7 @@ export = {
|
||||||
public_api: true,
|
public_api: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
void Container.get(InternalHooks).onUserRetrievedAllUsers(telemetryData);
|
Container.get(InternalHooks).onUserRetrievedAllUsers(telemetryData);
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
data: clean(users, { includeRole }),
|
data: clean(users, { includeRole }),
|
||||||
|
|
|
@ -58,7 +58,7 @@ export = {
|
||||||
);
|
);
|
||||||
|
|
||||||
await Container.get(ExternalHooks).run('workflow.afterCreate', [createdWorkflow]);
|
await Container.get(ExternalHooks).run('workflow.afterCreate', [createdWorkflow]);
|
||||||
void Container.get(InternalHooks).onWorkflowCreated(req.user, createdWorkflow, project, true);
|
Container.get(InternalHooks).onWorkflowCreated(req.user, createdWorkflow, project, true);
|
||||||
Container.get(EventService).emit('workflow-created', {
|
Container.get(EventService).emit('workflow-created', {
|
||||||
workflow: createdWorkflow,
|
workflow: createdWorkflow,
|
||||||
user: req.user,
|
user: req.user,
|
||||||
|
@ -101,7 +101,7 @@ export = {
|
||||||
return res.status(404).json({ message: 'Not Found' });
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container.get(InternalHooks).onUserRetrievedWorkflow({
|
Container.get(InternalHooks).onUserRetrievedWorkflow({
|
||||||
user_id: req.user.id,
|
user_id: req.user.id,
|
||||||
public_api: true,
|
public_api: true,
|
||||||
});
|
});
|
||||||
|
@ -163,7 +163,7 @@ export = {
|
||||||
...(!config.getEnv('workflowTagsDisabled') && { relations: ['tags'] }),
|
...(!config.getEnv('workflowTagsDisabled') && { relations: ['tags'] }),
|
||||||
});
|
});
|
||||||
|
|
||||||
void Container.get(InternalHooks).onUserRetrievedAllWorkflows({
|
Container.get(InternalHooks).onUserRetrievedAllWorkflows({
|
||||||
user_id: req.user.id,
|
user_id: req.user.id,
|
||||||
public_api: true,
|
public_api: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -112,7 +112,7 @@ export class UserManagementMailer {
|
||||||
|
|
||||||
this.logger.info('Sent workflow shared email successfully', { sharerId: sharer.id });
|
this.logger.info('Sent workflow shared email successfully', { sharerId: sharer.id });
|
||||||
|
|
||||||
void Container.get(InternalHooks).onUserTransactionalEmail({
|
Container.get(InternalHooks).onUserTransactionalEmail({
|
||||||
user_id: sharer.id,
|
user_id: sharer.id,
|
||||||
message_type: 'Workflow shared',
|
message_type: 'Workflow shared',
|
||||||
public_api: false,
|
public_api: false,
|
||||||
|
@ -120,7 +120,7 @@ export class UserManagementMailer {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
void Container.get(InternalHooks).onEmailFailed({
|
Container.get(InternalHooks).onEmailFailed({
|
||||||
user: sharer,
|
user: sharer,
|
||||||
message_type: 'Workflow shared',
|
message_type: 'Workflow shared',
|
||||||
public_api: false,
|
public_api: false,
|
||||||
|
@ -171,7 +171,7 @@ export class UserManagementMailer {
|
||||||
|
|
||||||
this.logger.info('Sent credentials shared email successfully', { sharerId: sharer.id });
|
this.logger.info('Sent credentials shared email successfully', { sharerId: sharer.id });
|
||||||
|
|
||||||
void Container.get(InternalHooks).onUserTransactionalEmail({
|
Container.get(InternalHooks).onUserTransactionalEmail({
|
||||||
user_id: sharer.id,
|
user_id: sharer.id,
|
||||||
message_type: 'Credentials shared',
|
message_type: 'Credentials shared',
|
||||||
public_api: false,
|
public_api: false,
|
||||||
|
@ -179,7 +179,7 @@ export class UserManagementMailer {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
void Container.get(InternalHooks).onEmailFailed({
|
Container.get(InternalHooks).onEmailFailed({
|
||||||
user: sharer,
|
user: sharer,
|
||||||
message_type: 'Credentials shared',
|
message_type: 'Credentials shared',
|
||||||
public_api: false,
|
public_api: false,
|
||||||
|
|
|
@ -51,7 +51,7 @@ export const handleLdapLogin = async (
|
||||||
await updateLdapUserOnLocalDb(identity, ldapAttributesValues);
|
await updateLdapUserOnLocalDb(identity, ldapAttributesValues);
|
||||||
} else {
|
} else {
|
||||||
const user = await createLdapUserOnLocalDb(ldapAttributesValues, ldapId);
|
const user = await createLdapUserOnLocalDb(ldapAttributesValues, ldapId);
|
||||||
void Container.get(InternalHooks).onUserSignup(user, {
|
Container.get(InternalHooks).onUserSignup(user, {
|
||||||
user_type: 'ldap',
|
user_type: 'ldap',
|
||||||
was_disabled_ldap_user: false,
|
was_disabled_ldap_user: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -55,7 +55,7 @@ export class ConcurrencyControlService {
|
||||||
|
|
||||||
this.productionQueue.on('concurrency-check', ({ capacity }) => {
|
this.productionQueue.on('concurrency-check', ({ capacity }) => {
|
||||||
if (this.shouldReport(capacity)) {
|
if (this.shouldReport(capacity)) {
|
||||||
void this.telemetry.track('User hit concurrency limit', {
|
this.telemetry.track('User hit concurrency limit', {
|
||||||
threshold: CLOUD_TEMP_PRODUCTION_LIMIT - capacity,
|
threshold: CLOUD_TEMP_PRODUCTION_LIMIT - capacity,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,7 @@ export class AuthController {
|
||||||
throw new BadRequestError('Invalid request');
|
throw new BadRequestError('Invalid request');
|
||||||
}
|
}
|
||||||
|
|
||||||
void this.internalHooks.onUserInviteEmailClick({ inviter, invitee });
|
this.internalHooks.onUserInviteEmailClick({ inviter, invitee });
|
||||||
this.eventService.emit('user-invite-email-click', { inviter, invitee });
|
this.eventService.emit('user-invite-email-click', { inviter, invitee });
|
||||||
|
|
||||||
const { firstName, lastName } = inviter;
|
const { firstName, lastName } = inviter;
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { Delete, Get, Middleware, Patch, Post, RestController, GlobalScope } fro
|
||||||
import { NodeRequest } from '@/requests';
|
import { NodeRequest } from '@/requests';
|
||||||
import type { InstalledPackages } from '@db/entities/InstalledPackages';
|
import type { InstalledPackages } from '@db/entities/InstalledPackages';
|
||||||
import type { CommunityPackages } from '@/Interfaces';
|
import type { CommunityPackages } from '@/Interfaces';
|
||||||
import { InternalHooks } from '@/InternalHooks';
|
|
||||||
import { Push } from '@/push';
|
import { Push } from '@/push';
|
||||||
import { CommunityPackagesService } from '@/services/communityPackages.service';
|
import { CommunityPackagesService } from '@/services/communityPackages.service';
|
||||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||||
|
@ -37,7 +36,6 @@ export function isNpmError(error: unknown): error is { code: number; stdout: str
|
||||||
export class CommunityPackagesController {
|
export class CommunityPackagesController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly push: Push,
|
private readonly push: Push,
|
||||||
private readonly internalHooks: InternalHooks,
|
|
||||||
private readonly communityPackagesService: CommunityPackagesService,
|
private readonly communityPackagesService: CommunityPackagesService,
|
||||||
private readonly eventService: EventService,
|
private readonly eventService: EventService,
|
||||||
) {}
|
) {}
|
||||||
|
|
|
@ -168,7 +168,7 @@ export class InvitationController {
|
||||||
|
|
||||||
this.authService.issueCookie(res, updatedUser, req.browserId);
|
this.authService.issueCookie(res, updatedUser, req.browserId);
|
||||||
|
|
||||||
void this.internalHooks.onUserSignup(updatedUser, {
|
this.internalHooks.onUserSignup(updatedUser, {
|
||||||
user_type: 'email',
|
user_type: 'email',
|
||||||
was_disabled_ldap_user: false,
|
was_disabled_ldap_user: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -101,7 +101,7 @@ export class MeController {
|
||||||
this.authService.issueCookie(res, user, req.browserId);
|
this.authService.issueCookie(res, user, req.browserId);
|
||||||
|
|
||||||
const fieldsChanged = Object.keys(payload);
|
const fieldsChanged = Object.keys(payload);
|
||||||
void this.internalHooks.onUserUpdate({ user, fields_changed: fieldsChanged });
|
this.internalHooks.onUserUpdate({ user, fields_changed: fieldsChanged });
|
||||||
this.eventService.emit('user-updated', { user, fieldsChanged });
|
this.eventService.emit('user-updated', { user, fieldsChanged });
|
||||||
|
|
||||||
const publicUser = await this.userService.toPublic(user);
|
const publicUser = await this.userService.toPublic(user);
|
||||||
|
@ -151,7 +151,7 @@ export class MeController {
|
||||||
|
|
||||||
this.authService.issueCookie(res, updatedUser, req.browserId);
|
this.authService.issueCookie(res, updatedUser, req.browserId);
|
||||||
|
|
||||||
void this.internalHooks.onUserUpdate({ user: updatedUser, fields_changed: ['password'] });
|
this.internalHooks.onUserUpdate({ user: updatedUser, fields_changed: ['password'] });
|
||||||
this.eventService.emit('user-updated', { user: updatedUser, fieldsChanged: ['password'] });
|
this.eventService.emit('user-updated', { user: updatedUser, fieldsChanged: ['password'] });
|
||||||
|
|
||||||
await this.externalHooks.run('user.password.update', [updatedUser.email, updatedUser.password]);
|
await this.externalHooks.run('user.password.update', [updatedUser.email, updatedUser.password]);
|
||||||
|
@ -186,7 +186,7 @@ export class MeController {
|
||||||
|
|
||||||
this.logger.info('User survey updated successfully', { userId: req.user.id });
|
this.logger.info('User survey updated successfully', { userId: req.user.id });
|
||||||
|
|
||||||
void this.internalHooks.onPersonalizationSurveySubmitted(req.user.id, personalizationAnswers);
|
this.internalHooks.onPersonalizationSurveySubmitted(req.user.id, personalizationAnswers);
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ export class OwnerController {
|
||||||
|
|
||||||
this.authService.issueCookie(res, owner, req.browserId);
|
this.authService.issueCookie(res, owner, req.browserId);
|
||||||
|
|
||||||
void this.internalHooks.onInstanceOwnerSetup({ user_id: owner.id });
|
this.internalHooks.onInstanceOwnerSetup({ user_id: owner.id });
|
||||||
|
|
||||||
return await this.userService.toPublic(owner, { posthog: this.postHog, withScopes: true });
|
return await this.userService.toPublic(owner, { posthog: this.postHog, withScopes: true });
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ export class PasswordResetController {
|
||||||
domain: this.urlService.getInstanceBaseUrl(),
|
domain: this.urlService.getInstanceBaseUrl(),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
void this.internalHooks.onEmailFailed({
|
this.internalHooks.onEmailFailed({
|
||||||
user,
|
user,
|
||||||
message_type: 'Reset password',
|
message_type: 'Reset password',
|
||||||
public_api: false,
|
public_api: false,
|
||||||
|
@ -132,13 +132,13 @@ export class PasswordResetController {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('Sent password reset email successfully', { userId: user.id, email });
|
this.logger.info('Sent password reset email successfully', { userId: user.id, email });
|
||||||
void this.internalHooks.onUserTransactionalEmail({
|
this.internalHooks.onUserTransactionalEmail({
|
||||||
user_id: id,
|
user_id: id,
|
||||||
message_type: 'Reset password',
|
message_type: 'Reset password',
|
||||||
public_api: false,
|
public_api: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
void this.internalHooks.onUserPasswordResetRequestClick({ user });
|
this.internalHooks.onUserPasswordResetRequestClick({ user });
|
||||||
this.eventService.emit('user-password-reset-request-click', { user });
|
this.eventService.emit('user-password-reset-request-click', { user });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ export class PasswordResetController {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info('Reset-password token resolved successfully', { userId: user.id });
|
this.logger.info('Reset-password token resolved successfully', { userId: user.id });
|
||||||
void this.internalHooks.onUserPasswordResetEmailClick({ user });
|
this.internalHooks.onUserPasswordResetEmailClick({ user });
|
||||||
this.eventService.emit('user-password-reset-email-click', { user });
|
this.eventService.emit('user-password-reset-email-click', { user });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,13 +215,13 @@ export class PasswordResetController {
|
||||||
|
|
||||||
this.authService.issueCookie(res, user, req.browserId);
|
this.authService.issueCookie(res, user, req.browserId);
|
||||||
|
|
||||||
void this.internalHooks.onUserUpdate({ user, fields_changed: ['password'] });
|
this.internalHooks.onUserUpdate({ user, fields_changed: ['password'] });
|
||||||
this.eventService.emit('user-updated', { user, fieldsChanged: ['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 users
|
||||||
const ldapIdentity = user?.authIdentities?.find((i) => i.providerType === 'ldap');
|
const ldapIdentity = user?.authIdentities?.find((i) => i.providerType === 'ldap');
|
||||||
if (ldapIdentity) {
|
if (ldapIdentity) {
|
||||||
void this.internalHooks.onUserSignup(user, {
|
this.internalHooks.onUserSignup(user, {
|
||||||
user_type: 'email',
|
user_type: 'email',
|
||||||
was_disabled_ldap_user: true,
|
was_disabled_ldap_user: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -253,7 +253,7 @@ export class UsersController {
|
||||||
await trx.delete(User, { id: userToDelete.id });
|
await trx.delete(User, { id: userToDelete.id });
|
||||||
});
|
});
|
||||||
|
|
||||||
void this.internalHooks.onUserDeletion({
|
this.internalHooks.onUserDeletion({
|
||||||
user: req.user,
|
user: req.user,
|
||||||
telemetryData,
|
telemetryData,
|
||||||
publicApi: false,
|
publicApi: false,
|
||||||
|
@ -294,7 +294,7 @@ export class UsersController {
|
||||||
|
|
||||||
await this.userService.update(targetUser.id, { role: payload.newRoleName });
|
await this.userService.update(targetUser.id, { role: payload.newRoleName });
|
||||||
|
|
||||||
void this.internalHooks.onUserRoleChange({
|
this.internalHooks.onUserRoleChange({
|
||||||
user: req.user,
|
user: req.user,
|
||||||
target_user_id: targetUser.id,
|
target_user_id: targetUser.id,
|
||||||
target_user_new_role: ['global', payload.newRoleName].join(' '),
|
target_user_new_role: ['global', payload.newRoleName].join(' '),
|
||||||
|
|
|
@ -244,7 +244,7 @@ export class FrontendService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getSettings(pushRef?: string): IN8nUISettings {
|
getSettings(pushRef?: string): IN8nUISettings {
|
||||||
void this.internalHooks.onFrontendSettingsAPI(pushRef);
|
this.internalHooks.onFrontendSettingsAPI(pushRef);
|
||||||
|
|
||||||
const restEndpoint = config.getEnv('endpoints.rest');
|
const restEndpoint = config.getEnv('endpoints.rest');
|
||||||
|
|
||||||
|
|
|
@ -144,14 +144,14 @@ export class UserService {
|
||||||
if (result.emailSent) {
|
if (result.emailSent) {
|
||||||
invitedUser.user.emailSent = true;
|
invitedUser.user.emailSent = true;
|
||||||
delete invitedUser.user?.inviteAcceptUrl;
|
delete invitedUser.user?.inviteAcceptUrl;
|
||||||
void Container.get(InternalHooks).onUserTransactionalEmail({
|
Container.get(InternalHooks).onUserTransactionalEmail({
|
||||||
user_id: id,
|
user_id: id,
|
||||||
message_type: 'New user invite',
|
message_type: 'New user invite',
|
||||||
public_api: false,
|
public_api: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container.get(InternalHooks).onUserInvite({
|
Container.get(InternalHooks).onUserInvite({
|
||||||
user: owner,
|
user: owner,
|
||||||
target_user_id: Object.values(toInviteUsers),
|
target_user_id: Object.values(toInviteUsers),
|
||||||
public_api: false,
|
public_api: false,
|
||||||
|
@ -164,7 +164,7 @@ export class UserService {
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
void Container.get(InternalHooks).onEmailFailed({
|
Container.get(InternalHooks).onEmailFailed({
|
||||||
user: owner,
|
user: owner,
|
||||||
message_type: 'New user invite',
|
message_type: 'New user invite',
|
||||||
public_api: false,
|
public_api: false,
|
||||||
|
|
|
@ -88,13 +88,12 @@ export class Telemetry {
|
||||||
); // every 6 hours
|
); // every 6 hours
|
||||||
}
|
}
|
||||||
|
|
||||||
private async pulse(): Promise<unknown> {
|
private async pulse() {
|
||||||
if (!this.rudderStack) {
|
if (!this.rudderStack) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const allPromises = Object.keys(this.executionCountsBuffer)
|
const workflowIdsToReport = Object.keys(this.executionCountsBuffer).filter((workflowId) => {
|
||||||
.filter((workflowId) => {
|
|
||||||
const data = this.executionCountsBuffer[workflowId];
|
const data = this.executionCountsBuffer[workflowId];
|
||||||
const sum =
|
const sum =
|
||||||
(data.manual_error?.count ?? 0) +
|
(data.manual_error?.count ?? 0) +
|
||||||
|
@ -102,16 +101,15 @@ export class Telemetry {
|
||||||
(data.prod_error?.count ?? 0) +
|
(data.prod_error?.count ?? 0) +
|
||||||
(data.prod_success?.count ?? 0);
|
(data.prod_success?.count ?? 0);
|
||||||
return sum > 0;
|
return sum > 0;
|
||||||
})
|
});
|
||||||
.map(async (workflowId) => {
|
|
||||||
const promise = this.track('Workflow execution count', {
|
for (const workflowId of workflowIdsToReport) {
|
||||||
|
this.track('Workflow execution count', {
|
||||||
event_version: '2',
|
event_version: '2',
|
||||||
workflow_id: workflowId,
|
workflow_id: workflowId,
|
||||||
...this.executionCountsBuffer[workflowId],
|
...this.executionCountsBuffer[workflowId],
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return await promise;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.executionCountsBuffer = {};
|
this.executionCountsBuffer = {};
|
||||||
|
|
||||||
|
@ -131,11 +129,11 @@ export class Telemetry {
|
||||||
team_projects: (await Container.get(ProjectRepository).getProjectCounts()).team,
|
team_projects: (await Container.get(ProjectRepository).getProjectCounts()).team,
|
||||||
project_role_count: await Container.get(ProjectRelationRepository).countUsersByRole(),
|
project_role_count: await Container.get(ProjectRelationRepository).countUsersByRole(),
|
||||||
};
|
};
|
||||||
allPromises.push(this.track('pulse', pulsePacket));
|
|
||||||
return await Promise.all(allPromises);
|
this.track('pulse', pulsePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
async trackWorkflowExecution(properties: IExecutionTrackProperties): Promise<void> {
|
trackWorkflowExecution(properties: IExecutionTrackProperties) {
|
||||||
if (this.rudderStack) {
|
if (this.rudderStack) {
|
||||||
const execTime = new Date();
|
const execTime = new Date();
|
||||||
const workflowId = properties.workflow_id;
|
const workflowId = properties.workflow_id;
|
||||||
|
@ -164,44 +162,42 @@ export class Telemetry {
|
||||||
properties.is_manual &&
|
properties.is_manual &&
|
||||||
properties.error_node_type?.startsWith('n8n-nodes-base')
|
properties.error_node_type?.startsWith('n8n-nodes-base')
|
||||||
) {
|
) {
|
||||||
void this.track('Workflow execution errored', properties);
|
this.track('Workflow execution errored', properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async trackN8nStop(): Promise<void> {
|
async trackN8nStop(): Promise<void> {
|
||||||
clearInterval(this.pulseIntervalReference);
|
clearInterval(this.pulseIntervalReference);
|
||||||
await this.track('User instance stopped');
|
|
||||||
void Promise.all([this.postHog.stop(), this.rudderStack?.flush()]);
|
this.track('User instance stopped');
|
||||||
|
|
||||||
|
await Promise.all([this.postHog.stop(), this.rudderStack?.flush()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
identify(traits?: { [key: string]: string | number | boolean | object | undefined | null }) {
|
||||||
|
if (!this.rudderStack) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
async identify(traits?: {
|
|
||||||
[key: string]: string | number | boolean | object | undefined | null;
|
|
||||||
}): Promise<void> {
|
|
||||||
const { instanceId } = this.instanceSettings;
|
const { instanceId } = this.instanceSettings;
|
||||||
return await new Promise<void>((resolve) => {
|
|
||||||
if (this.rudderStack) {
|
this.rudderStack.identify({
|
||||||
this.rudderStack.identify(
|
|
||||||
{
|
|
||||||
userId: instanceId,
|
userId: instanceId,
|
||||||
traits: { ...traits, instanceId },
|
traits: { ...traits, instanceId },
|
||||||
},
|
|
||||||
resolve,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async track(
|
track(
|
||||||
eventName: string,
|
eventName: string,
|
||||||
properties: ITelemetryTrackProperties = {},
|
properties: ITelemetryTrackProperties = {},
|
||||||
{ withPostHog } = { withPostHog: false }, // whether to additionally track with PostHog
|
{ withPostHog } = { withPostHog: false }, // whether to additionally track with PostHog
|
||||||
): Promise<void> {
|
) {
|
||||||
|
if (!this.rudderStack) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { instanceId } = this.instanceSettings;
|
const { instanceId } = this.instanceSettings;
|
||||||
return await new Promise<void>((resolve) => {
|
|
||||||
if (this.rudderStack) {
|
|
||||||
const { user_id } = properties;
|
const { user_id } = properties;
|
||||||
const updatedProperties = {
|
const updatedProperties = {
|
||||||
...properties,
|
...properties,
|
||||||
|
@ -219,11 +215,7 @@ export class Telemetry {
|
||||||
this.postHog?.track(payload);
|
this.postHog?.track(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.rudderStack.track(payload, resolve);
|
return this.rudderStack.track(payload);
|
||||||
}
|
|
||||||
|
|
||||||
return resolve();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// test helpers
|
// test helpers
|
||||||
|
|
|
@ -104,7 +104,7 @@ export class TelemetryEventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) {
|
private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) {
|
||||||
void this.telemetry.track('Project settings updated', {
|
this.telemetry.track('Project settings updated', {
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
role,
|
role,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
@ -120,7 +120,7 @@ export class TelemetryEventRelay {
|
||||||
removalType,
|
removalType,
|
||||||
targetProjectId,
|
targetProjectId,
|
||||||
}: Event['team-project-deleted']) {
|
}: Event['team-project-deleted']) {
|
||||||
void this.telemetry.track('User deleted project', {
|
this.telemetry.track('User deleted project', {
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
role,
|
role,
|
||||||
project_id: projectId,
|
project_id: projectId,
|
||||||
|
@ -130,7 +130,7 @@ export class TelemetryEventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private teamProjectCreated({ userId, role }: Event['team-project-created']) {
|
private teamProjectCreated({ userId, role }: Event['team-project-created']) {
|
||||||
void this.telemetry.track('User created project', {
|
this.telemetry.track('User created project', {
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
role,
|
role,
|
||||||
});
|
});
|
||||||
|
@ -142,7 +142,7 @@ export class TelemetryEventRelay {
|
||||||
repoType,
|
repoType,
|
||||||
connected,
|
connected,
|
||||||
}: Event['source-control-settings-updated']) {
|
}: Event['source-control-settings-updated']) {
|
||||||
void this.telemetry.track('User updated source control settings', {
|
this.telemetry.track('User updated source control settings', {
|
||||||
branch_name: branchName,
|
branch_name: branchName,
|
||||||
read_only_instance: readOnlyInstance,
|
read_only_instance: readOnlyInstance,
|
||||||
repo_type: repoType,
|
repo_type: repoType,
|
||||||
|
@ -155,7 +155,7 @@ export class TelemetryEventRelay {
|
||||||
workflowConflicts,
|
workflowConflicts,
|
||||||
credConflicts,
|
credConflicts,
|
||||||
}: Event['source-control-user-started-pull-ui']) {
|
}: Event['source-control-user-started-pull-ui']) {
|
||||||
void this.telemetry.track('User started pull via UI', {
|
this.telemetry.track('User started pull via UI', {
|
||||||
workflow_updates: workflowUpdates,
|
workflow_updates: workflowUpdates,
|
||||||
workflow_conflicts: workflowConflicts,
|
workflow_conflicts: workflowConflicts,
|
||||||
cred_conflicts: credConflicts,
|
cred_conflicts: credConflicts,
|
||||||
|
@ -165,7 +165,7 @@ export class TelemetryEventRelay {
|
||||||
private sourceControlUserFinishedPullUi({
|
private sourceControlUserFinishedPullUi({
|
||||||
workflowUpdates,
|
workflowUpdates,
|
||||||
}: Event['source-control-user-finished-pull-ui']) {
|
}: Event['source-control-user-finished-pull-ui']) {
|
||||||
void this.telemetry.track('User finished pull via UI', {
|
this.telemetry.track('User finished pull via UI', {
|
||||||
workflow_updates: workflowUpdates,
|
workflow_updates: workflowUpdates,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ export class TelemetryEventRelay {
|
||||||
workflow_updates: workflowUpdates,
|
workflow_updates: workflowUpdates,
|
||||||
forced,
|
forced,
|
||||||
});
|
});
|
||||||
void this.telemetry.track('User pulled via API', {
|
this.telemetry.track('User pulled via API', {
|
||||||
workflow_updates: workflowUpdates,
|
workflow_updates: workflowUpdates,
|
||||||
forced,
|
forced,
|
||||||
});
|
});
|
||||||
|
@ -191,7 +191,7 @@ export class TelemetryEventRelay {
|
||||||
credsEligibleWithConflicts,
|
credsEligibleWithConflicts,
|
||||||
variablesEligible,
|
variablesEligible,
|
||||||
}: Event['source-control-user-started-push-ui']) {
|
}: Event['source-control-user-started-push-ui']) {
|
||||||
void this.telemetry.track('User started push via UI', {
|
this.telemetry.track('User started push via UI', {
|
||||||
workflows_eligible: workflowsEligible,
|
workflows_eligible: workflowsEligible,
|
||||||
workflows_eligible_with_conflicts: workflowsEligibleWithConflicts,
|
workflows_eligible_with_conflicts: workflowsEligibleWithConflicts,
|
||||||
creds_eligible: credsEligible,
|
creds_eligible: credsEligible,
|
||||||
|
@ -206,7 +206,7 @@ export class TelemetryEventRelay {
|
||||||
credsPushed,
|
credsPushed,
|
||||||
variablesPushed,
|
variablesPushed,
|
||||||
}: Event['source-control-user-finished-push-ui']) {
|
}: Event['source-control-user-finished-push-ui']) {
|
||||||
void this.telemetry.track('User finished push via UI', {
|
this.telemetry.track('User finished push via UI', {
|
||||||
workflows_eligible: workflowsEligible,
|
workflows_eligible: workflowsEligible,
|
||||||
workflows_pushed: workflowsPushed,
|
workflows_pushed: workflowsPushed,
|
||||||
creds_pushed: credsPushed,
|
creds_pushed: credsPushed,
|
||||||
|
@ -215,13 +215,13 @@ export class TelemetryEventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private licenseRenewalAttempted({ success }: Event['license-renewal-attempted']) {
|
private licenseRenewalAttempted({ success }: Event['license-renewal-attempted']) {
|
||||||
void this.telemetry.track('Instance attempted to refresh license', {
|
this.telemetry.track('Instance attempted to refresh license', {
|
||||||
success,
|
success,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private variableCreated() {
|
private variableCreated() {
|
||||||
void this.telemetry.track('User created variable');
|
this.telemetry.track('User created variable');
|
||||||
}
|
}
|
||||||
|
|
||||||
private externalSecretsProviderSettingsSaved({
|
private externalSecretsProviderSettingsSaved({
|
||||||
|
@ -231,7 +231,7 @@ export class TelemetryEventRelay {
|
||||||
isNew,
|
isNew,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
}: Event['external-secrets-provider-settings-saved']) {
|
}: Event['external-secrets-provider-settings-saved']) {
|
||||||
void this.telemetry.track('User updated external secrets settings', {
|
this.telemetry.track('User updated external secrets settings', {
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
vault_type: vaultType,
|
vault_type: vaultType,
|
||||||
is_valid: isValid,
|
is_valid: isValid,
|
||||||
|
@ -241,7 +241,7 @@ export class TelemetryEventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private publicApiInvoked({ userId, path, method, apiVersion }: Event['public-api-invoked']) {
|
private publicApiInvoked({ userId, path, method, apiVersion }: Event['public-api-invoked']) {
|
||||||
void this.telemetry.track('User invoked API', {
|
this.telemetry.track('User invoked API', {
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
path,
|
path,
|
||||||
method,
|
method,
|
||||||
|
@ -252,7 +252,7 @@ export class TelemetryEventRelay {
|
||||||
private publicApiKeyCreated(event: Event['public-api-key-created']) {
|
private publicApiKeyCreated(event: Event['public-api-key-created']) {
|
||||||
const { user, publicApi } = event;
|
const { user, publicApi } = event;
|
||||||
|
|
||||||
void this.telemetry.track('API key created', {
|
this.telemetry.track('API key created', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
public_api: publicApi,
|
public_api: publicApi,
|
||||||
});
|
});
|
||||||
|
@ -261,7 +261,7 @@ export class TelemetryEventRelay {
|
||||||
private publicApiKeyDeleted(event: Event['public-api-key-deleted']) {
|
private publicApiKeyDeleted(event: Event['public-api-key-deleted']) {
|
||||||
const { user, publicApi } = event;
|
const { user, publicApi } = event;
|
||||||
|
|
||||||
void this.telemetry.track('API key deleted', {
|
this.telemetry.track('API key deleted', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
public_api: publicApi,
|
public_api: publicApi,
|
||||||
});
|
});
|
||||||
|
@ -278,7 +278,7 @@ export class TelemetryEventRelay {
|
||||||
packageAuthorEmail,
|
packageAuthorEmail,
|
||||||
failureReason,
|
failureReason,
|
||||||
}: Event['community-package-installed']) {
|
}: Event['community-package-installed']) {
|
||||||
void this.telemetry.track('cnr package install finished', {
|
this.telemetry.track('cnr package install finished', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
input_string: inputString,
|
input_string: inputString,
|
||||||
package_name: packageName,
|
package_name: packageName,
|
||||||
|
@ -300,7 +300,7 @@ export class TelemetryEventRelay {
|
||||||
packageAuthor,
|
packageAuthor,
|
||||||
packageAuthorEmail,
|
packageAuthorEmail,
|
||||||
}: Event['community-package-updated']) {
|
}: Event['community-package-updated']) {
|
||||||
void this.telemetry.track('cnr package updated', {
|
this.telemetry.track('cnr package updated', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
package_name: packageName,
|
package_name: packageName,
|
||||||
package_version_current: packageVersionCurrent,
|
package_version_current: packageVersionCurrent,
|
||||||
|
@ -319,7 +319,7 @@ export class TelemetryEventRelay {
|
||||||
packageAuthor,
|
packageAuthor,
|
||||||
packageAuthorEmail,
|
packageAuthorEmail,
|
||||||
}: Event['community-package-deleted']) {
|
}: Event['community-package-deleted']) {
|
||||||
void this.telemetry.track('cnr package deleted', {
|
this.telemetry.track('cnr package deleted', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
package_name: packageName,
|
package_name: packageName,
|
||||||
package_version: packageVersion,
|
package_version: packageVersion,
|
||||||
|
@ -336,7 +336,7 @@ export class TelemetryEventRelay {
|
||||||
projectId,
|
projectId,
|
||||||
projectType,
|
projectType,
|
||||||
}: Event['credentials-created']) {
|
}: Event['credentials-created']) {
|
||||||
void this.telemetry.track('User created credentials', {
|
this.telemetry.track('User created credentials', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
credential_type: credentialType,
|
credential_type: credentialType,
|
||||||
credential_id: credentialId,
|
credential_id: credentialId,
|
||||||
|
@ -353,7 +353,7 @@ export class TelemetryEventRelay {
|
||||||
userIdsShareesAdded,
|
userIdsShareesAdded,
|
||||||
shareesRemoved,
|
shareesRemoved,
|
||||||
}: Event['credentials-shared']) {
|
}: Event['credentials-shared']) {
|
||||||
void this.telemetry.track('User updated cred sharing', {
|
this.telemetry.track('User updated cred sharing', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
credential_type: credentialType,
|
credential_type: credentialType,
|
||||||
credential_id: credentialId,
|
credential_id: credentialId,
|
||||||
|
@ -364,7 +364,7 @@ export class TelemetryEventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private credentialsUpdated({ user, credentialId, credentialType }: Event['credentials-updated']) {
|
private credentialsUpdated({ user, credentialId, credentialType }: Event['credentials-updated']) {
|
||||||
void this.telemetry.track('User updated credentials', {
|
this.telemetry.track('User updated credentials', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
credential_type: credentialType,
|
credential_type: credentialType,
|
||||||
credential_id: credentialId,
|
credential_id: credentialId,
|
||||||
|
@ -372,7 +372,7 @@ export class TelemetryEventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private credentialsDeleted({ user, credentialId, credentialType }: Event['credentials-deleted']) {
|
private credentialsDeleted({ user, credentialId, credentialType }: Event['credentials-deleted']) {
|
||||||
void this.telemetry.track('User deleted credentials', {
|
this.telemetry.track('User deleted credentials', {
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
credential_type: credentialType,
|
credential_type: credentialType,
|
||||||
credential_id: credentialId,
|
credential_id: credentialId,
|
||||||
|
@ -385,7 +385,7 @@ export class TelemetryEventRelay {
|
||||||
usersSynced,
|
usersSynced,
|
||||||
error,
|
error,
|
||||||
}: Event['ldap-general-sync-finished']) {
|
}: Event['ldap-general-sync-finished']) {
|
||||||
void this.telemetry.track('Ldap general sync finished', {
|
this.telemetry.track('Ldap general sync finished', {
|
||||||
type,
|
type,
|
||||||
succeeded,
|
succeeded,
|
||||||
users_synced: usersSynced,
|
users_synced: usersSynced,
|
||||||
|
@ -407,7 +407,7 @@ export class TelemetryEventRelay {
|
||||||
loginLabel,
|
loginLabel,
|
||||||
loginEnabled,
|
loginEnabled,
|
||||||
}: Event['ldap-settings-updated']) {
|
}: Event['ldap-settings-updated']) {
|
||||||
void this.telemetry.track('User updated Ldap settings', {
|
this.telemetry.track('User updated Ldap settings', {
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
loginIdAttribute,
|
loginIdAttribute,
|
||||||
firstNameAttribute,
|
firstNameAttribute,
|
||||||
|
@ -424,11 +424,11 @@ export class TelemetryEventRelay {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ldapLoginSyncFailed({ error }: Event['ldap-login-sync-failed']) {
|
private ldapLoginSyncFailed({ error }: Event['ldap-login-sync-failed']) {
|
||||||
void this.telemetry.track('Ldap login sync failed', { error });
|
this.telemetry.track('Ldap login sync failed', { error });
|
||||||
}
|
}
|
||||||
|
|
||||||
private loginFailedDueToLdapDisabled({ userId }: Event['login-failed-due-to-ldap-disabled']) {
|
private loginFailedDueToLdapDisabled({ userId }: Event['login-failed-due-to-ldap-disabled']) {
|
||||||
void this.telemetry.track('User login failed since ldap disabled', { user_ud: userId });
|
this.telemetry.track('User login failed since ldap disabled', { user_ud: userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async serverStarted() {
|
private async serverStarted() {
|
||||||
|
@ -489,12 +489,10 @@ export class TelemetryEventRelay {
|
||||||
where: {},
|
where: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
void Promise.all([
|
this.telemetry.identify(info);
|
||||||
this.telemetry.identify(info),
|
|
||||||
this.telemetry.track('Instance started', {
|
this.telemetry.track('Instance started', {
|
||||||
...info,
|
...info,
|
||||||
earliest_workflow_created: firstWorkflow?.createdAt,
|
earliest_workflow_created: firstWorkflow?.createdAt,
|
||||||
}),
|
});
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,7 +282,7 @@ export class WorkflowService {
|
||||||
await this.workflowRepository.delete(workflowId);
|
await this.workflowRepository.delete(workflowId);
|
||||||
await this.binaryDataService.deleteMany(idsForDeletion);
|
await this.binaryDataService.deleteMany(idsForDeletion);
|
||||||
|
|
||||||
void Container.get(InternalHooks).onWorkflowDeleted(user, workflowId, false);
|
Container.get(InternalHooks).onWorkflowDeleted(user, workflowId, false);
|
||||||
this.eventService.emit('workflow-deleted', { user, workflowId });
|
this.eventService.emit('workflow-deleted', { user, workflowId });
|
||||||
await this.externalHooks.run('workflow.afterDelete', [workflowId]);
|
await this.externalHooks.run('workflow.afterDelete', [workflowId]);
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,7 @@ export class WorkflowsController {
|
||||||
delete savedWorkflowWithMetaData.shared;
|
delete savedWorkflowWithMetaData.shared;
|
||||||
|
|
||||||
await this.externalHooks.run('workflow.afterCreate', [savedWorkflow]);
|
await this.externalHooks.run('workflow.afterCreate', [savedWorkflow]);
|
||||||
void this.internalHooks.onWorkflowCreated(req.user, newWorkflow, project!, false);
|
this.internalHooks.onWorkflowCreated(req.user, newWorkflow, project!, false);
|
||||||
this.eventService.emit('workflow-created', { user: req.user, workflow: newWorkflow });
|
this.eventService.emit('workflow-created', { user: req.user, workflow: newWorkflow });
|
||||||
|
|
||||||
const scopes = await this.workflowService.getWorkflowScopes(req.user, savedWorkflow.id);
|
const scopes = await this.workflowService.getWorkflowScopes(req.user, savedWorkflow.id);
|
||||||
|
@ -454,7 +454,7 @@ export class WorkflowsController {
|
||||||
newShareeIds = toShare;
|
newShareeIds = toShare;
|
||||||
});
|
});
|
||||||
|
|
||||||
void this.internalHooks.onWorkflowSharingUpdate(workflowId, req.user.id, shareWithIds);
|
this.internalHooks.onWorkflowSharingUpdate(workflowId, req.user.id, shareWithIds);
|
||||||
|
|
||||||
const projectsRelations = await this.projectRelationRepository.findBy({
|
const projectsRelations = await this.projectRelationRepository.findBy({
|
||||||
projectId: In(newShareeIds),
|
projectId: In(newShareeIds),
|
||||||
|
|
|
@ -14,11 +14,12 @@ describe('Telemetry', () => {
|
||||||
let startPulseSpy: jest.SpyInstance;
|
let startPulseSpy: jest.SpyInstance;
|
||||||
const spyTrack = jest.spyOn(Telemetry.prototype, 'track').mockName('track');
|
const spyTrack = jest.spyOn(Telemetry.prototype, 'track').mockName('track');
|
||||||
|
|
||||||
const mockRudderStack: Pick<RudderStack, 'flush' | 'identify' | 'track'> = {
|
const mockRudderStack = mock<RudderStack>();
|
||||||
flush: (resolve) => resolve?.(),
|
mockRudderStack.track.mockImplementation(function (_, cb) {
|
||||||
identify: (data, resolve) => resolve?.(),
|
cb?.();
|
||||||
track: (data, resolve) => resolve?.(),
|
|
||||||
};
|
return this;
|
||||||
|
});
|
||||||
|
|
||||||
let telemetry: Telemetry;
|
let telemetry: Telemetry;
|
||||||
const instanceId = 'Telemetry unit test';
|
const instanceId = 'Telemetry unit test';
|
||||||
|
@ -26,9 +27,9 @@ describe('Telemetry', () => {
|
||||||
const instanceSettings = mockInstance(InstanceSettings, { instanceId });
|
const instanceSettings = mockInstance(InstanceSettings, { instanceId });
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
startPulseSpy = jest
|
// @ts-expect-error Spying on private method
|
||||||
.spyOn(Telemetry.prototype as any, 'startPulse')
|
startPulseSpy = jest.spyOn(Telemetry.prototype, 'startPulse').mockImplementation(() => {});
|
||||||
.mockImplementation(() => {});
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
jest.setSystemTime(testDateTime);
|
jest.setSystemTime(testDateTime);
|
||||||
config.set('diagnostics.enabled', true);
|
config.set('diagnostics.enabled', true);
|
||||||
|
@ -49,7 +50,8 @@ describe('Telemetry', () => {
|
||||||
await postHog.init();
|
await postHog.init();
|
||||||
|
|
||||||
telemetry = new Telemetry(mock(), postHog, mock(), instanceSettings, mock());
|
telemetry = new Telemetry(mock(), postHog, mock(), instanceSettings, mock());
|
||||||
(telemetry as any).rudderStack = mockRudderStack;
|
// @ts-expect-error Assigning to private property
|
||||||
|
telemetry.rudderStack = mockRudderStack;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
@ -79,30 +81,30 @@ describe('Telemetry', () => {
|
||||||
payload.is_manual = true;
|
payload.is_manual = true;
|
||||||
payload.success = true;
|
payload.success = true;
|
||||||
const execTime1 = fakeJestSystemTime('2022-01-01 12:00:00');
|
const execTime1 = fakeJestSystemTime('2022-01-01 12:00:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
fakeJestSystemTime('2022-01-01 12:30:00');
|
fakeJestSystemTime('2022-01-01 12:30:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
payload.is_manual = false;
|
payload.is_manual = false;
|
||||||
payload.success = true;
|
payload.success = true;
|
||||||
const execTime2 = fakeJestSystemTime('2022-01-01 13:00:00');
|
const execTime2 = fakeJestSystemTime('2022-01-01 13:00:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
fakeJestSystemTime('2022-01-01 12:30:00');
|
fakeJestSystemTime('2022-01-01 12:30:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
payload.is_manual = true;
|
payload.is_manual = true;
|
||||||
payload.success = false;
|
payload.success = false;
|
||||||
const execTime3 = fakeJestSystemTime('2022-01-01 14:00:00');
|
const execTime3 = fakeJestSystemTime('2022-01-01 14:00:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
fakeJestSystemTime('2022-01-01 12:30:00');
|
fakeJestSystemTime('2022-01-01 12:30:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
payload.is_manual = false;
|
payload.is_manual = false;
|
||||||
payload.success = false;
|
payload.success = false;
|
||||||
const execTime4 = fakeJestSystemTime('2022-01-01 15:00:00');
|
const execTime4 = fakeJestSystemTime('2022-01-01 15:00:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
fakeJestSystemTime('2022-01-01 12:30:00');
|
fakeJestSystemTime('2022-01-01 12:30:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
expect(spyTrack).toHaveBeenCalledTimes(0);
|
expect(spyTrack).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
@ -127,9 +129,9 @@ describe('Telemetry', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const execTime1 = fakeJestSystemTime('2022-01-01 12:00:00');
|
const execTime1 = fakeJestSystemTime('2022-01-01 12:00:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
fakeJestSystemTime('2022-01-01 12:30:00');
|
fakeJestSystemTime('2022-01-01 12:30:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
let execBuffer = telemetry.getCountsBuffer();
|
let execBuffer = telemetry.getCountsBuffer();
|
||||||
|
|
||||||
|
@ -140,9 +142,9 @@ describe('Telemetry', () => {
|
||||||
|
|
||||||
payload.error_node_type = 'n8n-nodes-base.node-type';
|
payload.error_node_type = 'n8n-nodes-base.node-type';
|
||||||
fakeJestSystemTime('2022-01-01 13:00:00');
|
fakeJestSystemTime('2022-01-01 13:00:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
fakeJestSystemTime('2022-01-01 12:30:00');
|
fakeJestSystemTime('2022-01-01 12:30:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
execBuffer = telemetry.getCountsBuffer();
|
execBuffer = telemetry.getCountsBuffer();
|
||||||
|
|
||||||
|
@ -163,7 +165,7 @@ describe('Telemetry', () => {
|
||||||
|
|
||||||
// successful execution
|
// successful execution
|
||||||
const execTime1 = fakeJestSystemTime('2022-01-01 12:00:00');
|
const execTime1 = fakeJestSystemTime('2022-01-01 12:00:00');
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
expect(spyTrack).toHaveBeenCalledTimes(0);
|
expect(spyTrack).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
@ -179,7 +181,7 @@ describe('Telemetry', () => {
|
||||||
payload.error_node_type = 'n8n-nodes-base.merge';
|
payload.error_node_type = 'n8n-nodes-base.merge';
|
||||||
payload.workflow_id = '2';
|
payload.workflow_id = '2';
|
||||||
|
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
expect(spyTrack).toHaveBeenCalledTimes(0);
|
expect(spyTrack).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
@ -198,12 +200,12 @@ describe('Telemetry', () => {
|
||||||
payload.error_node_type = 'n8n-nodes-base.merge';
|
payload.error_node_type = 'n8n-nodes-base.merge';
|
||||||
payload.workflow_id = '2';
|
payload.workflow_id = '2';
|
||||||
|
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
payload.error_node_type = 'n8n-nodes-base.merge';
|
payload.error_node_type = 'n8n-nodes-base.merge';
|
||||||
payload.workflow_id = '1';
|
payload.workflow_id = '1';
|
||||||
|
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
expect(spyTrack).toHaveBeenCalledTimes(0);
|
expect(spyTrack).toHaveBeenCalledTimes(0);
|
||||||
execBuffer = telemetry.getCountsBuffer();
|
execBuffer = telemetry.getCountsBuffer();
|
||||||
|
@ -225,7 +227,7 @@ describe('Telemetry', () => {
|
||||||
const execTime2 = fakeJestSystemTime('2022-01-01 12:00:00');
|
const execTime2 = fakeJestSystemTime('2022-01-01 12:00:00');
|
||||||
payload.error_node_type = 'custom-package.custom-node';
|
payload.error_node_type = 'custom-package.custom-node';
|
||||||
payload.success = false;
|
payload.success = false;
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
expect(spyTrack).toHaveBeenCalledTimes(0);
|
expect(spyTrack).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
@ -249,7 +251,7 @@ describe('Telemetry', () => {
|
||||||
payload.success = false;
|
payload.success = false;
|
||||||
payload.error_node_type = 'n8n-nodes-base.merge';
|
payload.error_node_type = 'n8n-nodes-base.merge';
|
||||||
payload.is_manual = true;
|
payload.is_manual = true;
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
expect(spyTrack).toHaveBeenCalledTimes(1);
|
expect(spyTrack).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
@ -327,27 +329,27 @@ describe('Telemetry', () => {
|
||||||
error_node_type: 'custom-nodes-base.node-type',
|
error_node_type: 'custom-nodes-base.node-type',
|
||||||
};
|
};
|
||||||
|
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
payload.is_manual = false;
|
payload.is_manual = false;
|
||||||
payload.success = true;
|
payload.success = true;
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
payload.is_manual = true;
|
payload.is_manual = true;
|
||||||
payload.success = false;
|
payload.success = false;
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
payload.is_manual = false;
|
payload.is_manual = false;
|
||||||
payload.success = false;
|
payload.success = false;
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
payload.workflow_id = '2';
|
payload.workflow_id = '2';
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
await telemetry.trackWorkflowExecution(payload);
|
telemetry.trackWorkflowExecution(payload);
|
||||||
|
|
||||||
expect(spyTrack).toHaveBeenCalledTimes(0);
|
expect(spyTrack).toHaveBeenCalledTimes(0);
|
||||||
expect(pulseSpy).toBeCalledTimes(0);
|
expect(pulseSpy).toBeCalledTimes(0);
|
||||||
|
|
Loading…
Reference in a new issue