chore(ai-builder): Telemetry updates for pinned data (#22625)

This commit is contained in:
Michael Drury
2025-12-04 12:54:36 +00:00
committed by GitHub
parent 727a12da56
commit 0866f644b1
5 changed files with 93 additions and 7 deletions
@@ -384,6 +384,64 @@ describe('Telemetry', () => {
}),
);
});
test('should include instance_id, version_cli, and user_id in track properties', () => {
const eventName = 'Test Event';
const properties = { user_id: '1234', custom_prop: 'value' };
telemetry.track(eventName, properties);
expect(mockRudderStack.track).toHaveBeenCalledWith(
expect.objectContaining({
event: eventName,
properties: expect.objectContaining({
instance_id: instanceId,
user_id: '1234',
version_cli: expect.any(String),
custom_prop: 'value',
}),
}),
);
});
test('should format userId with user_id when provided', () => {
const eventName = 'Test Event';
const properties = { user_id: '5678' };
telemetry.track(eventName, properties);
expect(mockRudderStack.track).toHaveBeenCalledWith(
expect.objectContaining({
userId: `${instanceId}#5678`,
}),
);
});
test('should format userId without user_id when not provided', () => {
const eventName = 'Test Event';
telemetry.track(eventName, {});
expect(mockRudderStack.track).toHaveBeenCalledWith(
expect.objectContaining({
userId: instanceId,
}),
);
});
test('should set user_id to undefined when not provided in properties', () => {
const eventName = 'Test Event';
telemetry.track(eventName, {});
expect(mockRudderStack.track).toHaveBeenCalledWith(
expect.objectContaining({
properties: expect.objectContaining({
user_id: undefined,
}),
}),
);
});
});
});
+1
View File
@@ -225,6 +225,7 @@ export class Telemetry {
const updatedProperties = {
...properties,
instance_id: instanceId,
user_id: user_id ?? undefined,
version_cli: N8N_VERSION,
};
@@ -102,16 +102,35 @@ describe('usePinnedData', () => {
});
describe('onSetDataSuccess()', () => {
it('should trigger telemetry on successful data setting', async () => {
it('should trigger telemetry on successful data setting with correct payload values', async () => {
const workflowsStore = useWorkflowsStore();
workflowsStore.workflow.id = 'test-workflow-id';
const telemetry = useTelemetry();
const spy = vi.spyOn(telemetry, 'track');
const pinnedData = usePinnedData(ref({ name: 'testNode', type: 'someType' } as INodeUi), {
const node = ref({
name: 'testNode',
type: 'n8n-nodes-base.httpRequest',
id: 'test-node-id',
} as INodeUi);
const pinnedData = usePinnedData(node, {
displayMode: ref('json'),
runIndex: ref(0),
runIndex: ref(2),
});
pinnedData.onSetDataSuccess({ source: 'pin-icon-click' });
expect(spy).toHaveBeenCalled();
expect(spy).toHaveBeenCalledWith(
'Ndv data pinning success',
expect.objectContaining({
pinning_source: 'pin-icon-click',
node_type: 'n8n-nodes-base.httpRequest',
view: 'json',
run_index: 2,
workflow_id: 'test-workflow-id',
node_id: 'test-node-id',
}),
);
});
});
@@ -215,6 +215,8 @@ export function usePinnedData(
data_size: stringSizeInBytes(data.value),
view: displayMode,
run_index: runIndex,
workflow_id: workflowsStore.workflowId,
node_id: targetNode?.id,
};
void externalHooks.run('runData.onDataPinningSuccess', telemetryPayload);
@@ -95,6 +95,7 @@ import {
N8nTooltip,
} from '@n8n/design-system';
import { injectWorkflowState } from '@/app/composables/useWorkflowState';
const LazyRunDataTable = defineAsyncComponent(async () => await import('./RunDataTable.vue'));
const LazyRunDataJson = defineAsyncComponent(async () => await import('./RunDataJson.vue'));
@@ -807,6 +808,7 @@ function getResolvedNodeOutputs() {
}
return [];
}
function shouldHintBeDisplayed(hint: NodeHint): boolean {
const { location, whenToDisplay } = hint;
@@ -963,6 +965,8 @@ function enterEditMode({ origin }: EnterEditModeArgs) {
is_output_present: hasNodeRun.value || pinnedData.hasData.value,
view: !hasNodeRun.value && !pinnedData.hasData.value ? 'undefined' : props.displayMode,
is_data_pinned: pinnedData.hasData.value,
workflow_id: workflowsStore.workflowId,
node_id: activeNode.value?.id,
});
}
@@ -1038,6 +1042,8 @@ async function onTogglePinData({ source }: { source: PinDataSource | UnpinDataSo
push_ref: props.pushRef,
run_index: props.runIndex,
view: !hasNodeRun.value && !pinnedData.hasData.value ? 'none' : props.displayMode,
workflow_id: workflowsStore.workflowId,
node_id: activeNode.value?.id,
};
void externalHooks.run('runData.onTogglePinData', telemetryPayload);
@@ -1725,9 +1731,9 @@ defineExpose({ enterEditMode });
</div>
<div v-else-if="editMode.enabled" :class="$style.editMode">
<N8nText v-if="previousExecutionDataUsedInEditMode" class="mb-2xs" size="small">{{
i18n.baseText('runData.pinData.insertedExecutionData')
}}</N8nText>
<N8nText v-if="previousExecutionDataUsedInEditMode" class="mb-2xs" size="small"
>{{ i18n.baseText('runData.pinData.insertedExecutionData') }}
</N8nText>
<div :class="[$style.editModeBody, 'ignore-key-press-canvas']">
<JsonEditor
:model-value="editMode.value"