feat: Track n8n Connect credential toggle in telemetry (no-changelog) (#30245)

This commit is contained in:
Michael Kret
2026-05-12 14:09:29 +03:00
committed by GitHub
parent cd0519f360
commit 27d72acae5
2 changed files with 87 additions and 2 deletions
@@ -26,6 +26,12 @@ import {
createWorkflowDocumentId,
} from '@/app/stores/workflowDocument.store';
const trackMock = vi.hoisted(() => vi.fn());
vi.mock('@/app/composables/useTelemetry', () => ({
useTelemetry: () => ({ track: trackMock }),
}));
vi.mock('@/app/composables/useAiGateway', () => ({
useAiGateway: vi.fn(() => ({
isEnabled: ref(false),
@@ -1289,6 +1295,76 @@ describe('NodeCredentials', () => {
expect((payload.properties.credentials['googlePalmApi'] as { id: string }).id).toBe('cred-1');
});
describe('telemetry', () => {
const toggleOnStub = {
template: '<button data-test-id="ai-gateway-toggle-on" @click="$emit(\'toggle\', true)" />',
props: ['aiGatewayEnabled'],
emits: ['toggle'],
};
const toggleOffStub = {
template:
'<button data-test-id="ai-gateway-toggle-off" @click="$emit(\'toggle\', false)" />',
props: ['aiGatewayEnabled'],
emits: ['toggle'],
};
it('should track telemetry with mode "n8n_connect" when toggled ON by user', async () => {
ndvStore.activeNode = googleAiNode;
renderComponent({
props: { node: googleAiNode, overrideCredType: 'googlePalmApi' },
global: { stubs: { AiGatewaySelector: toggleOnStub } },
});
await userEvent.click(screen.getByTestId('ai-gateway-toggle-on'));
expect(trackMock).toHaveBeenCalledWith('User toggled n8n connect credential', {
credential_type: 'googlePalmApi',
node_type: googleAiNode.type,
mode: 'n8n_connect',
workflow_id: expect.any(String),
});
});
it('should track telemetry with mode "own" when toggled OFF by user', async () => {
const nodeWithGateway: INodeUi = {
...googleAiNode,
credentials: { googlePalmApi: { id: null, name: '', __aiGatewayManaged: true } },
};
ndvStore.activeNode = nodeWithGateway;
renderComponent({
props: { node: nodeWithGateway, overrideCredType: 'googlePalmApi' },
global: { stubs: { AiGatewaySelector: toggleOffStub } },
});
await userEvent.click(screen.getByTestId('ai-gateway-toggle-off'));
expect(trackMock).toHaveBeenCalledWith('User toggled n8n connect credential', {
credential_type: 'googlePalmApi',
node_type: googleAiNode.type,
mode: 'own',
workflow_id: expect.any(String),
});
});
it('should not track telemetry when toggled ON automatically on mount', () => {
// No credentials — auto-select path calls onAiGatewaySelector with isUserAction=false
ndvStore.activeNode = googleAiNode;
renderComponent({
props: { node: googleAiNode, overrideCredType: 'googlePalmApi' },
global: { stubs: { AiGatewaySelector: toggleOnStub } },
});
expect(trackMock).not.toHaveBeenCalledWith(
'User toggled n8n connect credential',
expect.anything(),
);
});
});
it('should emit credentialSelected removing credentials when toggled OFF with no available credentials', async () => {
credentialsStore.state.credentials = {};
@@ -224,7 +224,7 @@ watch(
if (aiGateway.isEnabled.value) {
for (const { type } of types) {
if (aiGateway.isCredentialTypeSupported(type.name)) {
onAiGatewaySelector(type.name, true);
onAiGatewaySelector(type.name, true, false);
}
}
}
@@ -542,7 +542,7 @@ function showAiGatewaySelector(credentialType: string): boolean {
return true;
}
function onAiGatewaySelector(credentialType: string, enable: boolean): void {
function onAiGatewaySelector(credentialType: string, enable: boolean, isUserAction = true): void {
const credentials = { ...(props.node.credentials ?? {}) };
if (enable) {
@@ -564,6 +564,15 @@ function onAiGatewaySelector(credentialType: string, enable: boolean): void {
}
}
if (isUserAction) {
telemetry.track('User toggled n8n connect credential', {
credential_type: credentialType,
node_type: props.node.type,
mode: enable ? 'n8n_connect' : 'own',
workflow_id: props.standalone ? '' : workflowsStore.workflowId,
});
}
emit('credentialSelected', {
name: props.node.name,
properties: { credentials },