Skip to content

Commit

Permalink
fix(editor): Add telemetry to source control feature (#13016)
Browse files Browse the repository at this point in the history
  • Loading branch information
cstuncsik authored Feb 4, 2025
1 parent cc907fb commit 18eaa54
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ async function pullWorkfolder() {
>
<template #content>
<N8nText tag="div" class="mb-xs">
These resources will be updated or deleted, and any local changes to them will be lost. To
keep the local version, push it before pulling.
{{ i18n.baseText('settings.sourceControl.modals.pull.description') }}
<br />
<N8nLink :to="i18n.baseText('settings.sourceControl.docs.using.pushPull.url')">
{{ i18n.baseText('settings.sourceControl.modals.push.description.learnMore') }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { SourceControlledFile } from '@n8n/api-types';
import { useSourceControlStore } from '@/stores/sourceControl.store';
import { mockedStore } from '@/__tests__/utils';
import { VIEWS } from '@/constants';
import { useTelemetry } from '@/composables/useTelemetry';

const eventBus = createEventBus();

Expand All @@ -22,7 +23,19 @@ vi.mock('vue-router', () => ({
useRouter: vi.fn(),
}));

vi.mock('@/composables/useTelemetry', () => {
const track = vi.fn();
return {
useTelemetry: () => {
return {
track,
};
},
};
});

let route: ReturnType<typeof useRoute>;
let telemetry: ReturnType<typeof useTelemetry>;

const DynamicScrollerStub = {
props: {
Expand Down Expand Up @@ -59,7 +72,9 @@ const renderModal = createComponentRenderer(SourceControlPushModal, {

describe('SourceControlPushModal', () => {
beforeEach(() => {
vi.clearAllMocks();
route = useRoute();
telemetry = useTelemetry();
createTestingPinia();
});

Expand Down Expand Up @@ -319,9 +334,12 @@ describe('SourceControlPushModal', () => {
expect(getAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(2);

await userEvent.type(getByTestId('source-control-push-search'), '1');
await waitFor(() =>
expect(getAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(1),
);
await waitFor(() => {
expect(getAllByTestId('source-control-push-modal-file-checkbox')).toHaveLength(1);
expect(telemetry.track).toHaveBeenCalledWith('User searched workflows in commit modal', {
search: '1',
});
});
});

it('should filter by status', async () => {
Expand Down Expand Up @@ -379,6 +397,9 @@ describe('SourceControlPushModal', () => {
const items = getAllByTestId('source-control-push-modal-file-checkbox');
expect(items).toHaveLength(1);
expect(items[0]).toHaveTextContent('Created Workflow');
expect(telemetry.track).toHaveBeenCalledWith('User filtered by status in commit modal', {
status: 'created',
});
});
});

Expand Down
35 changes: 19 additions & 16 deletions packages/editor-ui/src/components/SourceControlPushModal.ee.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import Modal from './Modal.vue';
import { SOURCE_CONTROL_PUSH_MODAL_KEY, VIEWS } from '@/constants';
import { computed, onMounted, ref, toRaw } from 'vue';
import { computed, onMounted, ref, toRaw, watch } from 'vue';
import type { EventBus } from 'n8n-design-system/utils';
import { useI18n } from '@/composables/useI18n';
import { useLoadingService } from '@/composables/useLoadingService';
Expand Down Expand Up @@ -37,6 +37,7 @@ import {
} from '@n8n/api-types';
import { orderBy, groupBy } from 'lodash-es';
import { getStatusText, getStatusTheme, getPushPriorityByStatus } from '@/utils/sourceControlUtils';
import { useTelemetry } from '@/composables/useTelemetry';
const props = defineProps<{
data: { eventBus: EventBus; status: SourceControlledFile[] };
Expand All @@ -48,6 +49,7 @@ const toast = useToast();
const i18n = useI18n();
const sourceControlStore = useSourceControlStore();
const route = useRoute();
const telemetry = useTelemetry();
const concatenateWithAnd = (messages: string[]) =>
new Intl.ListFormat(i18n.locale, { style: 'long', type: 'conjunction' }).format(messages);
Expand Down Expand Up @@ -191,16 +193,12 @@ const filteredWorkflows = computed(() => {
return false;
}
if (filters.value.status && filters.value.status !== workflow.status) {
return false;
}
return true;
return !(filters.value.status && filters.value.status !== workflow.status);
});
});
const sortedWorkflows = computed(() => {
const sorted = orderBy(
const sortedWorkflows = computed(() =>
orderBy(
filteredWorkflows.value,
[
// keep the current workflow at the top of the list
Expand All @@ -209,10 +207,8 @@ const sortedWorkflows = computed(() => {
'updatedAt',
],
['desc', 'asc', 'desc'],
);
return sorted;
});
),
);
const commitMessage = ref('');
const isSubmitDisabled = computed(() => {
Expand All @@ -225,11 +221,8 @@ const isSubmitDisabled = computed(() => {
changes.value.tags.length +
changes.value.variables.length +
selectedChanges.value.size;
if (toBePushed <= 0) {
return true;
}
return false;
return toBePushed <= 0;
});
const sortedWorkflowsSet = computed(() => new Set(sortedWorkflows.value.map(({ id }) => id)));
Expand Down Expand Up @@ -354,6 +347,16 @@ async function commitAndPush() {
}
const modalHeight = computed(() => (changes.value.workflows.length ? 'min(80vh, 850px)' : 'auto'));
watch(
() => filters.value.status,
(status) => {
telemetry.track('User filtered by status in commit modal', { status });
},
);
watch(refDebounced(search, 500), (term) => {
telemetry.track('User searched workflows in commit modal', { search: term });
});
</script>

<template>
Expand Down
14 changes: 14 additions & 0 deletions packages/editor-ui/src/components/layouts/ResourcesListLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,20 @@ watch(
() => callDebounced(sendFiltersTelemetry, { debounceTime: 1000, trailing: true }, 'search'),
);
watch(
() => filtersModel.value.setupNeeded,
() => {
sendFiltersTelemetry('setupNeeded');
},
);
watch(
() => filtersModel.value.incomplete,
() => {
sendFiltersTelemetry('incomplete');
},
);
watch(
() => sortBy.value,
(newValue) => {
Expand Down
23 changes: 19 additions & 4 deletions packages/editor-ui/src/utils/sourceControlUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { BaseTextKey } from '@/plugins/i18n';
import { VIEWS } from '@/constants';
import { groupBy } from 'lodash-es';
import type { useToast } from '@/composables/useToast';
import { telemetry } from '@/plugins/telemetry';

type SourceControlledFileStatus = SourceControlledFile['status'];

Expand Down Expand Up @@ -47,17 +48,31 @@ export const getPushPriorityByStatus = (status: SourceControlledFileStatus) =>

const variablesToast = {
title: i18n.baseText('settings.sourceControl.pull.upToDate.variables.title'),
message: h(RouterLink, { to: { name: VIEWS.VARIABLES }, query: { incomplete: 'true' } }, () =>
i18n.baseText('settings.sourceControl.pull.upToDate.variables.description'),
message: h(
RouterLink,
{
to: { name: VIEWS.VARIABLES, query: { incomplete: 'true' } },
onClick: () => {
telemetry.track('User clicked review variables');
},
},
() => i18n.baseText('settings.sourceControl.pull.upToDate.variables.description'),
),
type: 'info' as const,
duration: 0,
};

const credentialsToast = {
title: i18n.baseText('settings.sourceControl.pull.upToDate.credentials.title'),
message: h(RouterLink, { to: { name: VIEWS.CREDENTIALS, query: { setupNeeded: 'true' } } }, () =>
i18n.baseText('settings.sourceControl.pull.upToDate.credentials.description'),
message: h(
RouterLink,
{
to: { name: VIEWS.CREDENTIALS, query: { setupNeeded: 'true' } },
onClick: () => {
telemetry.track('User clicked review credentials');
},
},
() => i18n.baseText('settings.sourceControl.pull.upToDate.credentials.description'),
),
type: 'info' as const,
duration: 0,
Expand Down

0 comments on commit 18eaa54

Please sign in to comment.