mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Add telemetry to source control feature (#13016)
This commit is contained in:
parent
cc907fbca9
commit
18eaa5423d
|
@ -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') }}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
@ -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: {
|
||||
|
@ -59,7 +72,9 @@ const renderModal = createComponentRenderer(SourceControlPushModal, {
|
|||
|
||||
describe('SourceControlPushModal', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
route = useRoute();
|
||||
telemetry = useTelemetry();
|
||||
createTestingPinia();
|
||||
});
|
||||
|
||||
|
@ -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 () => {
|
||||
|
@ -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',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
@ -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[] };
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -209,10 +207,8 @@ const sortedWorkflows = computed(() => {
|
|||
'updatedAt',
|
||||
],
|
||||
['desc', 'asc', 'desc'],
|
||||
);
|
||||
|
||||
return sorted;
|
||||
});
|
||||
),
|
||||
);
|
||||
|
||||
const commitMessage = ref('');
|
||||
const isSubmitDisabled = computed(() => {
|
||||
|
@ -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)));
|
||||
|
@ -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>
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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'];
|
||||
|
||||
|
@ -47,8 +48,15 @@ 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,
|
||||
|
@ -56,8 +64,15 @@ const variablesToast = {
|
|||
|
||||
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,
|
||||
|
|
Loading…
Reference in a new issue