fix(editor): Add telemetry to workflow history (#7811)

This commit is contained in:
Csaba Tuncsik 2023-11-29 09:09:21 +01:00 committed by GitHub
parent c0633987bf
commit d4970410e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 15 deletions

View file

@ -1754,7 +1754,8 @@ export type CloudUpdateLinkSourceType =
| 'usage_page' | 'usage_page'
| 'settings-users' | 'settings-users'
| 'variables' | 'variables'
| 'community-nodes'; | 'community-nodes'
| 'workflow-history';
export type UTMCampaign = export type UTMCampaign =
| 'upgrade-custom-data-filter' | 'upgrade-custom-data-filter'
@ -1770,7 +1771,8 @@ export type UTMCampaign =
| 'open' | 'open'
| 'upgrade-users' | 'upgrade-users'
| 'upgrade-variables' | 'upgrade-variables'
| 'upgrade-community-nodes'; | 'upgrade-community-nodes'
| 'upgrade-workflow-history';
export type N8nBanners = { export type N8nBanners = {
[key in BannerName]: { [key in BannerName]: {

View file

@ -17,6 +17,7 @@ import WorkflowHistoryContent from '@/components/WorkflowHistory/WorkflowHistory
import { useWorkflowHistoryStore } from '@/stores/workflowHistory.store'; import { useWorkflowHistoryStore } from '@/stores/workflowHistory.store';
import { useUIStore } from '@/stores/ui.store'; import { useUIStore } from '@/stores/ui.store';
import { useWorkflowsStore } from '@/stores/workflows.store'; import { useWorkflowsStore } from '@/stores/workflows.store';
import { telemetry } from '@/plugins/telemetry';
type WorkflowHistoryActionRecord = { type WorkflowHistoryActionRecord = {
[K in Uppercase<WorkflowHistoryActionTypes[number]>]: Lowercase<K>; [K in Uppercase<WorkflowHistoryActionTypes[number]>]: Lowercase<K>;
@ -73,6 +74,12 @@ const isFirstItemShown = computed(
); );
const evaluatedPruneTime = computed(() => Math.floor(workflowHistoryStore.evaluatedPruneTime / 24)); 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 loadMore = async (queryParams: WorkflowHistoryRequestParams) => {
const history = await workflowHistoryStore.getWorkflowHistory( const history = await workflowHistoryStore.getWorkflowHistory(
route.params.workflowId, route.params.workflowId,
@ -83,6 +90,7 @@ const loadMore = async (queryParams: WorkflowHistoryRequestParams) => {
}; };
onBeforeMount(async () => { onBeforeMount(async () => {
sendTelemetry('User opened workflow history');
try { try {
const [workflow] = await Promise.all([ const [workflow] = await Promise.all([
workflowsStore.fetchWorkflow(route.params.workflowId), workflowsStore.fetchWorkflow(route.params.workflowId),
@ -233,15 +241,19 @@ const onAction = async ({
switch (action) { switch (action) {
case WORKFLOW_HISTORY_ACTIONS.OPEN: case WORKFLOW_HISTORY_ACTIONS.OPEN:
openInNewTab(id); openInNewTab(id);
sendTelemetry('User opened version in new tab');
break; break;
case WORKFLOW_HISTORY_ACTIONS.DOWNLOAD: case WORKFLOW_HISTORY_ACTIONS.DOWNLOAD:
await workflowHistoryStore.downloadVersion(route.params.workflowId, id, data); await workflowHistoryStore.downloadVersion(route.params.workflowId, id, data);
sendTelemetry('User downloaded version');
break; break;
case WORKFLOW_HISTORY_ACTIONS.CLONE: case WORKFLOW_HISTORY_ACTIONS.CLONE:
await cloneWorkflowVersion(id, data); await cloneWorkflowVersion(id, data);
sendTelemetry('User cloned version');
break; break;
case WORKFLOW_HISTORY_ACTIONS.RESTORE: case WORKFLOW_HISTORY_ACTIONS.RESTORE:
await restoreWorkflowVersion(id, data); await restoreWorkflowVersion(id, data);
sendTelemetry('User restored version');
break; break;
} }
} catch (error) { } catch (error) {
@ -259,6 +271,7 @@ const onAction = async ({
const onPreview = async ({ event, id }: { event: MouseEvent; id: WorkflowVersionId }) => { const onPreview = async ({ event, id }: { event: MouseEvent; id: WorkflowVersionId }) => {
if (event.metaKey || event.ctrlKey) { if (event.metaKey || event.ctrlKey) {
openInNewTab(id); openInNewTab(id);
sendTelemetry('User opened version in new tab');
} else { } else {
await router.push({ await router.push({
name: VIEWS.WORKFLOW_HISTORY, name: VIEWS.WORKFLOW_HISTORY,
@ -283,6 +296,7 @@ watchEffect(async () => {
route.params.workflowId, route.params.workflowId,
route.params.versionId, route.params.versionId,
); );
sendTelemetry('User selected version');
} catch (error) { } catch (error) {
toast.showError( toast.showError(
new Error(`${error.message} "${route.params.versionId}"&nbsp;`), new Error(`${error.message} "${route.params.versionId}"&nbsp;`),

View file

@ -17,6 +17,7 @@ import {
} from '@/stores/__tests__/utils/workflowHistoryTestUtils'; } from '@/stores/__tests__/utils/workflowHistoryTestUtils';
import type { WorkflowVersion } from '@/types/workflowHistory'; import type { WorkflowVersion } from '@/types/workflowHistory';
import type { IWorkflowDb } from '@/Interface'; import type { IWorkflowDb } from '@/Interface';
import { telemetry } from '@/plugins/telemetry';
vi.mock('vue-router', () => { vi.mock('vue-router', () => {
const params = {}; const params = {};
@ -56,7 +57,9 @@ const renderComponent = createComponentRenderer(WorkflowHistoryPage, {
}, },
template: `<div> template: `<div>
<button data-test-id="stub-preview-button" @click="event => $emit('preview', {id, event})" /> <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-clone-button" @click="() => $emit('action', { action: 'clone', id })" />
<button data-test-id="stub-download-button" @click="() => $emit('action', { action: 'download', id })" />
</div>`, </div>`,
}), }),
}, },
@ -85,6 +88,7 @@ describe('WorkflowHistory', () => {
vi.spyOn(workflowsStore, 'fetchWorkflow').mockResolvedValue({} as IWorkflowDb); vi.spyOn(workflowsStore, 'fetchWorkflow').mockResolvedValue({} as IWorkflowDb);
vi.spyOn(workflowHistoryStore, 'getWorkflowHistory').mockResolvedValue(historyData); vi.spyOn(workflowHistoryStore, 'getWorkflowHistory').mockResolvedValue(historyData);
vi.spyOn(workflowHistoryStore, 'getWorkflowVersion').mockResolvedValue(versionData); vi.spyOn(workflowHistoryStore, 'getWorkflowVersion').mockResolvedValue(versionData);
vi.spyOn(telemetry, 'track').mockImplementation(() => {});
windowOpenSpy = vi.spyOn(window, 'open').mockImplementation(() => null); windowOpenSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
}); });
@ -97,12 +101,15 @@ describe('WorkflowHistory', () => {
renderComponent({ pinia }); renderComponent({ pinia });
await waitFor(() => await waitFor(() => {
expect(router.replace).toHaveBeenCalledWith({ expect(router.replace).toHaveBeenCalledWith({
name: VIEWS.WORKFLOW_HISTORY, name: VIEWS.WORKFLOW_HISTORY,
params: { workflowId, versionId: versionData.versionId }, 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 () => { it('should load version data if path contains /:versionId', async () => {
@ -113,8 +120,13 @@ describe('WorkflowHistory', () => {
renderComponent({ pinia }); renderComponent({ pinia });
await waitFor(() => expect(router.replace).not.toHaveBeenCalled());
expect(getWorkflowVersionSpy).toHaveBeenCalledWith(workflowId, versionData.versionId); 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 () => { it('should change path on preview', async () => {
@ -124,12 +136,33 @@ describe('WorkflowHistory', () => {
await userEvent.click(getByTestId('stub-preview-button')); await userEvent.click(getByTestId('stub-preview-button'));
await waitFor(() => await waitFor(() => {
expect(router.push).toHaveBeenCalledWith({ expect(router.push).toHaveBeenCalledWith({
name: VIEWS.WORKFLOW_HISTORY, name: VIEWS.WORKFLOW_HISTORY,
params: { workflowId, versionId }, 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 () => { it('should open preview in new tab if meta key used', async () => {
@ -141,12 +174,15 @@ describe('WorkflowHistory', () => {
await user.keyboard('[ControlLeft>]'); await user.keyboard('[ControlLeft>]');
await user.click(getByTestId('stub-preview-button')); await user.click(getByTestId('stub-preview-button'));
await waitFor(() => await waitFor(() => {
expect(router.resolve).toHaveBeenCalledWith({ expect(router.resolve).toHaveBeenCalledWith({
name: VIEWS.WORKFLOW_HISTORY, name: VIEWS.WORKFLOW_HISTORY,
params: { workflowId, versionId }, params: { workflowId, versionId },
}), });
); expect(telemetry.track).toHaveBeenCalledWith('User opened version in new tab', {
workflow_id: workflowId,
});
});
expect(windowOpenSpy).toHaveBeenCalled(); expect(windowOpenSpy).toHaveBeenCalled();
}); });
@ -160,13 +196,29 @@ describe('WorkflowHistory', () => {
const { getByTestId, getByRole } = renderComponent({ pinia }); const { getByTestId, getByRole } = renderComponent({ pinia });
await userEvent.click(getByTestId('stub-clone-button')); await userEvent.click(getByTestId('stub-clone-button'));
await waitFor(() => await waitFor(() => {
expect(router.resolve).toHaveBeenCalledWith({ expect(router.resolve).toHaveBeenCalledWith({
name: VIEWS.WORKFLOW, name: VIEWS.WORKFLOW,
params: { name: newWorkflowId }, params: { name: newWorkflowId },
}), });
); expect(telemetry.track).toHaveBeenCalledWith('User cloned version', {
workflow_id: workflowId,
});
});
expect(within(getByRole('alert')).getByRole('link')).toBeInTheDocument(); 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,
});
});
});
}); });