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(editor): Add telemetry to workflow history #7811

Merged
merged 4 commits into from
Nov 29, 2023
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
6 changes: 4 additions & 2 deletions packages/editor-ui/src/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1754,7 +1754,8 @@ export type CloudUpdateLinkSourceType =
| 'usage_page'
| 'settings-users'
| 'variables'
| 'community-nodes';
| 'community-nodes'
| 'workflow-history';

export type UTMCampaign =
| 'upgrade-custom-data-filter'
Expand All @@ -1770,7 +1771,8 @@ export type UTMCampaign =
| 'open'
| 'upgrade-users'
| 'upgrade-variables'
| 'upgrade-community-nodes';
| 'upgrade-community-nodes'
| 'upgrade-workflow-history';

export type N8nBanners = {
[key in BannerName]: {
Expand Down
14 changes: 14 additions & 0 deletions packages/editor-ui/src/views/WorkflowHistory.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import WorkflowHistoryContent from '@/components/WorkflowHistory/WorkflowHistory
import { useWorkflowHistoryStore } from '@/stores/workflowHistory.store';
import { useUIStore } from '@/stores/ui.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { telemetry } from '@/plugins/telemetry';

type WorkflowHistoryActionRecord = {
[K in Uppercase<WorkflowHistoryActionTypes[number]>]: Lowercase<K>;
Expand Down Expand Up @@ -73,6 +74,12 @@ const isFirstItemShown = computed(
);
const evaluatedPruneTime = computed(() => Math.floor(workflowHistoryStore.evaluatedPruneTime / 24));

const sendTelemetry = (event: string) => {
telemetry.track(event, {
workflow_id: route.params.workflowId,
});
};

const loadMore = async (queryParams: WorkflowHistoryRequestParams) => {
const history = await workflowHistoryStore.getWorkflowHistory(
route.params.workflowId,
Expand All @@ -83,6 +90,7 @@ const loadMore = async (queryParams: WorkflowHistoryRequestParams) => {
};

onBeforeMount(async () => {
sendTelemetry('User opened workflow history');
try {
const [workflow] = await Promise.all([
workflowsStore.fetchWorkflow(route.params.workflowId),
Expand Down Expand Up @@ -233,15 +241,19 @@ const onAction = async ({
switch (action) {
case WORKFLOW_HISTORY_ACTIONS.OPEN:
openInNewTab(id);
sendTelemetry('User opened version in new tab');
break;
case WORKFLOW_HISTORY_ACTIONS.DOWNLOAD:
await workflowHistoryStore.downloadVersion(route.params.workflowId, id, data);
sendTelemetry('User downloaded version');
break;
case WORKFLOW_HISTORY_ACTIONS.CLONE:
await cloneWorkflowVersion(id, data);
sendTelemetry('User cloned version');
break;
case WORKFLOW_HISTORY_ACTIONS.RESTORE:
await restoreWorkflowVersion(id, data);
sendTelemetry('User restored version');
break;
}
} catch (error) {
Expand All @@ -259,6 +271,7 @@ const onAction = async ({
const onPreview = async ({ event, id }: { event: MouseEvent; id: WorkflowVersionId }) => {
if (event.metaKey || event.ctrlKey) {
openInNewTab(id);
sendTelemetry('User opened version in new tab');
} else {
await router.push({
name: VIEWS.WORKFLOW_HISTORY,
Expand All @@ -283,6 +296,7 @@ watchEffect(async () => {
route.params.workflowId,
route.params.versionId,
);
sendTelemetry('User selected version');
} catch (error) {
toast.showError(
new Error(`${error.message} "${route.params.versionId}"&nbsp;`),
Expand Down
78 changes: 65 additions & 13 deletions packages/editor-ui/src/views/__tests__/WorkflowHistory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
} from '@/stores/__tests__/utils/workflowHistoryTestUtils';
import type { WorkflowVersion } from '@/types/workflowHistory';
import type { IWorkflowDb } from '@/Interface';
import { telemetry } from '@/plugins/telemetry';

vi.mock('vue-router', () => {
const params = {};
Expand Down Expand Up @@ -56,7 +57,9 @@ const renderComponent = createComponentRenderer(WorkflowHistoryPage, {
},
template: `<div>
<button data-test-id="stub-preview-button" @click="event => $emit('preview', {id, event})" />
<button data-test-id="stub-open-button" @click="() => $emit('action', { action: 'open', id })" />
<button data-test-id="stub-clone-button" @click="() => $emit('action', { action: 'clone', id })" />
<button data-test-id="stub-download-button" @click="() => $emit('action', { action: 'download', id })" />
</div>`,
}),
},
Expand Down Expand Up @@ -85,6 +88,7 @@ describe('WorkflowHistory', () => {
vi.spyOn(workflowsStore, 'fetchWorkflow').mockResolvedValue({} as IWorkflowDb);
vi.spyOn(workflowHistoryStore, 'getWorkflowHistory').mockResolvedValue(historyData);
vi.spyOn(workflowHistoryStore, 'getWorkflowVersion').mockResolvedValue(versionData);
vi.spyOn(telemetry, 'track').mockImplementation(() => {});
windowOpenSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
});

Expand All @@ -97,12 +101,15 @@ describe('WorkflowHistory', () => {

renderComponent({ pinia });

await waitFor(() =>
await waitFor(() => {
expect(router.replace).toHaveBeenCalledWith({
name: VIEWS.WORKFLOW_HISTORY,
params: { workflowId, versionId: versionData.versionId },
}),
);
});
expect(telemetry.track).toHaveBeenCalledWith('User opened workflow history', {
workflow_id: workflowId,
});
});
});

it('should load version data if path contains /:versionId', async () => {
Expand All @@ -113,8 +120,13 @@ describe('WorkflowHistory', () => {

renderComponent({ pinia });

await waitFor(() => expect(router.replace).not.toHaveBeenCalled());
expect(getWorkflowVersionSpy).toHaveBeenCalledWith(workflowId, versionData.versionId);
await waitFor(() => {
expect(router.replace).not.toHaveBeenCalled();
expect(telemetry.track).toHaveBeenCalledWith('User selected version', {
workflow_id: workflowId,
});
});
});

it('should change path on preview', async () => {
Expand All @@ -124,12 +136,33 @@ describe('WorkflowHistory', () => {

await userEvent.click(getByTestId('stub-preview-button'));

await waitFor(() =>
await waitFor(() => {
expect(router.push).toHaveBeenCalledWith({
name: VIEWS.WORKFLOW_HISTORY,
params: { workflowId, versionId },
}),
);
});
expect(telemetry.track).toHaveBeenCalledWith('User selected version', {
workflow_id: workflowId,
});
});
});

it('should open preview in new tab if open action is dispatched', async () => {
route.params.workflowId = workflowId;
const { getByTestId } = renderComponent({ pinia });

await userEvent.click(getByTestId('stub-open-button'));

await waitFor(() => {
expect(router.resolve).toHaveBeenCalledWith({
name: VIEWS.WORKFLOW_HISTORY,
params: { workflowId, versionId },
});
expect(telemetry.track).toHaveBeenCalledWith('User opened version in new tab', {
workflow_id: workflowId,
});
});
expect(windowOpenSpy).toHaveBeenCalled();
});

it('should open preview in new tab if meta key used', async () => {
Expand All @@ -141,12 +174,15 @@ describe('WorkflowHistory', () => {
await user.keyboard('[ControlLeft>]');
await user.click(getByTestId('stub-preview-button'));

await waitFor(() =>
await waitFor(() => {
expect(router.resolve).toHaveBeenCalledWith({
name: VIEWS.WORKFLOW_HISTORY,
params: { workflowId, versionId },
}),
);
});
expect(telemetry.track).toHaveBeenCalledWith('User opened version in new tab', {
workflow_id: workflowId,
});
});
expect(windowOpenSpy).toHaveBeenCalled();
});

Expand All @@ -160,13 +196,29 @@ describe('WorkflowHistory', () => {
const { getByTestId, getByRole } = renderComponent({ pinia });
await userEvent.click(getByTestId('stub-clone-button'));

await waitFor(() =>
await waitFor(() => {
expect(router.resolve).toHaveBeenCalledWith({
name: VIEWS.WORKFLOW,
params: { name: newWorkflowId },
}),
);
});
expect(telemetry.track).toHaveBeenCalledWith('User cloned version', {
workflow_id: workflowId,
});
});

expect(within(getByRole('alert')).getByRole('link')).toBeInTheDocument();
});

it('should download workflow version', async () => {
route.params.workflowId = workflowId;

const { getByTestId } = renderComponent({ pinia });
await userEvent.click(getByTestId('stub-download-button'));

await waitFor(() => {
expect(telemetry.track).toHaveBeenCalledWith('User downloaded version', {
workflow_id: workflowId,
});
});
});
});
Loading