mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(editor): Remove Segment (#9878)
This commit is contained in:
parent
13d83f2037
commit
10f7d4b5b9
|
@ -88,7 +88,6 @@ import ParameterOptions from '@/components/ParameterOptions.vue';
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
import { useToast } from '@/composables/useToast';
|
import { useToast } from '@/composables/useToast';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
import { useSegment } from '@/stores/segment.store';
|
|
||||||
import { getMappedResult } from '@/utils/mappingUtils';
|
import { getMappedResult } from '@/utils/mappingUtils';
|
||||||
import { hasExpressionMapping, hasOnlyListMode, isValueExpression } from '@/utils/nodeTypesUtils';
|
import { hasExpressionMapping, hasOnlyListMode, isValueExpression } from '@/utils/nodeTypesUtils';
|
||||||
import { isResourceLocatorValue } from '@/utils/typeGuards';
|
import { isResourceLocatorValue } from '@/utils/typeGuards';
|
||||||
|
@ -272,9 +271,6 @@ function onDrop(newParamValue: string) {
|
||||||
hasExpressionMapping(prevValue),
|
hasExpressionMapping(prevValue),
|
||||||
success: true,
|
success: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const segment = useSegment();
|
|
||||||
segment.track(segment.EVENTS.MAPPED_DATA);
|
|
||||||
}
|
}
|
||||||
forceShowExpression.value = false;
|
forceShowExpression.value = false;
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
|
@ -39,7 +39,6 @@ import { useI18n } from '@/composables/useI18n';
|
||||||
import { useToast } from '@/composables/useToast';
|
import { useToast } from '@/composables/useToast';
|
||||||
import * as NodeViewUtils from '@/utils/nodeViewUtils';
|
import * as NodeViewUtils from '@/utils/nodeViewUtils';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { useSegment } from '@/stores/segment.store';
|
|
||||||
import type { Ref } from 'vue';
|
import type { Ref } from 'vue';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
|
@ -650,7 +649,6 @@ export function useCanvasOperations({
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
void externalHooks.run('nodeView.addNodeButton', { nodeTypeName: node.type });
|
void externalHooks.run('nodeView.addNodeButton', { nodeTypeName: node.type });
|
||||||
useSegment().trackAddedTrigger(node.type);
|
|
||||||
const trackProperties: ITelemetryTrackProperties = {
|
const trackProperties: ITelemetryTrackProperties = {
|
||||||
node_type: node.type,
|
node_type: node.type,
|
||||||
node_version: newNodeData.typeVersion,
|
node_version: newNodeData.typeVersion,
|
||||||
|
|
|
@ -32,7 +32,6 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import { parse } from 'flatted';
|
import { parse } from 'flatted';
|
||||||
import { useSegment } from '@/stores/segment.store';
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useOrchestrationStore } from '@/stores/orchestration.store';
|
import { useOrchestrationStore } from '@/stores/orchestration.store';
|
||||||
import { usePushConnectionStore } from '@/stores/pushConnection.store';
|
import { usePushConnectionStore } from '@/stores/pushConnection.store';
|
||||||
|
@ -58,7 +57,6 @@ export function usePushConnection({ router }: { router: ReturnType<typeof useRou
|
||||||
const orchestrationManagerStore = useOrchestrationStore();
|
const orchestrationManagerStore = useOrchestrationStore();
|
||||||
const pushStore = usePushConnectionStore();
|
const pushStore = usePushConnectionStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
const segmentStore = useSegment();
|
|
||||||
const uiStore = useUIStore();
|
const uiStore = useUIStore();
|
||||||
const workflowsStore = useWorkflowsStore();
|
const workflowsStore = useWorkflowsStore();
|
||||||
|
|
||||||
|
@ -521,9 +519,6 @@ export function usePushConnection({ router }: { router: ReturnType<typeof useRou
|
||||||
runDataExecutedStartData: runDataExecuted.data.startData,
|
runDataExecutedStartData: runDataExecuted.data.startData,
|
||||||
resultDataError: runDataExecuted.data.resultData.error,
|
resultDataError: runDataExecuted.data.resultData.error,
|
||||||
});
|
});
|
||||||
if (!runDataExecuted.data.resultData.error) {
|
|
||||||
segmentStore.trackSuccessfulWorkflowExecution(runDataExecuted);
|
|
||||||
}
|
|
||||||
} else if (receivedData.type === 'executionStarted') {
|
} else if (receivedData.type === 'executionStarted') {
|
||||||
const pushData = receivedData.data;
|
const pushData = receivedData.data;
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,5 @@
|
||||||
import { hooksAddFakeDoorFeatures } from '@/hooks/utils';
|
import { hooksAddFakeDoorFeatures } from '@/hooks/utils';
|
||||||
import {
|
|
||||||
getAuthenticationModalEventData,
|
|
||||||
getExpressionEditorEventsData,
|
|
||||||
getInsertedItemFromExpEditorEventData,
|
|
||||||
getNodeTypeChangedEventData,
|
|
||||||
getOpenWorkflowSettingsEventData,
|
|
||||||
getOutputModeChangedEventData,
|
|
||||||
getUpdatedWorkflowSettingsEventData,
|
|
||||||
getUserSavedCredentialsEventData,
|
|
||||||
getExecutionFinishedEventData,
|
|
||||||
getNodeRemovedEventData,
|
|
||||||
getNodeEditingFinishedEventData,
|
|
||||||
getExecutionStartedEventData,
|
|
||||||
} from '@/hooks/segment';
|
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
||||||
import {
|
|
||||||
hooksGenerateNodesPanelEvent,
|
|
||||||
hooksResetNodesPanelSession,
|
|
||||||
nodesPanelSession,
|
|
||||||
} from '@/hooks/utils/hooksNodesPanel';
|
|
||||||
import { useSegment } from '@/stores/segment.store';
|
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
import type { IDataObject } from 'n8n-workflow';
|
|
||||||
import type { INodeUi } from '@/Interface';
|
|
||||||
import type { ExternalHooks } from '@/types';
|
import type { ExternalHooks } from '@/types';
|
||||||
|
|
||||||
export const n8nCloudHooks: PartialDeep<ExternalHooks> = {
|
export const n8nCloudHooks: PartialDeep<ExternalHooks> = {
|
||||||
|
@ -34,474 +10,4 @@ export const n8nCloudHooks: PartialDeep<ExternalHooks> = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
nodeView: {
|
|
||||||
mount: [
|
|
||||||
() => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
segmentStore.identify();
|
|
||||||
},
|
|
||||||
],
|
|
||||||
createNodeActiveChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
source: meta.source,
|
|
||||||
nodes_panel_session_id: nodesPanelSession.pushRef,
|
|
||||||
};
|
|
||||||
|
|
||||||
hooksResetNodesPanelSession();
|
|
||||||
segmentStore.track('User opened nodes panel', eventData);
|
|
||||||
segmentStore.page('Cloud instance', 'Nodes panel', eventData);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
addNodeButton: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User added node to workflow canvas',
|
|
||||||
properties: {
|
|
||||||
node_type: meta.nodeTypeName.split('.')[1],
|
|
||||||
nodes_panel_session_id: nodesPanelSession.pushRef,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
main: {
|
|
||||||
routeChange: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const splitPath = meta.to.path.split('/');
|
|
||||||
if (meta.from.path !== '/' && splitPath[1] === 'workflow') {
|
|
||||||
const eventData = {
|
|
||||||
workflow_id: splitPath[2],
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.page('Cloud instance', 'Workflow editor', eventData);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
credential: {
|
|
||||||
saved: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = getUserSavedCredentialsEventData(meta);
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
credentialsEdit: {
|
|
||||||
credentialTypeChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
if (meta.newValue) {
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User opened Credentials modal',
|
|
||||||
properties: {
|
|
||||||
source: meta.setCredentialType === meta.credentialType ? 'node' : 'primary_menu',
|
|
||||||
new_credential: !meta.editCredentials,
|
|
||||||
credential_type: meta.credentialType,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
segmentStore.page('Cloud instance', 'Credentials modal', eventData.properties);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
credentialModalOpened: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User opened Credentials modal',
|
|
||||||
properties: {
|
|
||||||
source: meta.activeNode ? 'node' : 'primary_menu',
|
|
||||||
new_credential: !meta.isEditingCredential,
|
|
||||||
credential_type: meta.credentialType,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
segmentStore.page('Cloud instance', 'Credentials modal', eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
credentialsList: {
|
|
||||||
mounted: [
|
|
||||||
() => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User opened global Credentials panel',
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName);
|
|
||||||
segmentStore.page('Cloud instance', 'Credentials panel');
|
|
||||||
},
|
|
||||||
],
|
|
||||||
dialogVisibleChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
if (meta.dialogVisible) {
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User opened global Credentials panel',
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName);
|
|
||||||
segmentStore.page('Cloud instance', 'Credentials panel');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
workflowSettings: {
|
|
||||||
dialogVisibleChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
if (meta.dialogVisible) {
|
|
||||||
const eventData = getOpenWorkflowSettingsEventData();
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
saveSettings: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = getUpdatedWorkflowSettingsEventData(meta);
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
dataDisplay: {
|
|
||||||
onDocumentationUrlClick: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User clicked node modal docs link',
|
|
||||||
properties: {
|
|
||||||
node_type: meta.nodeType.name.split('.')[1],
|
|
||||||
docs_link: meta.documentationUrl,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
nodeTypeChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const ndvStore = useNDVStore();
|
|
||||||
const eventData = getNodeTypeChangedEventData(meta);
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
segmentStore.page('Cloud instance', 'Node modal', {
|
|
||||||
node: ndvStore.activeNode?.name,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
],
|
|
||||||
nodeEditingFinished: [
|
|
||||||
() => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const ndvStore = useNDVStore();
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
|
||||||
|
|
||||||
const eventData = getNodeEditingFinishedEventData(ndvStore.activeNode);
|
|
||||||
if (eventData) {
|
|
||||||
eventData.properties!.workflow_id = workflowsStore.workflowId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventData) {
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
executionsList: {
|
|
||||||
openDialog: [
|
|
||||||
() => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User opened Executions log',
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName);
|
|
||||||
segmentStore.page('Cloud instance', 'Executions log');
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
showMessage: {
|
|
||||||
showError: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'Instance FE emitted error',
|
|
||||||
properties: {
|
|
||||||
error_title: meta.title,
|
|
||||||
error_description: meta.message,
|
|
||||||
error_message: meta.errorMessage,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
expressionEdit: {
|
|
||||||
itemSelected: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = getInsertedItemFromExpEditorEventData(meta);
|
|
||||||
|
|
||||||
if (meta.selectedItem.variable.startsWith('Object.keys')) {
|
|
||||||
eventData.properties!.variable_type = 'Keys';
|
|
||||||
} else if (meta.selectedItem.variable.startsWith('Object.values')) {
|
|
||||||
eventData.properties!.variable_type = 'Values';
|
|
||||||
} else {
|
|
||||||
eventData.properties!.variable_type = 'Raw value';
|
|
||||||
}
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
dialogVisibleChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const currentValue = meta.value?.slice(1) ?? '';
|
|
||||||
let isValueDefault = false;
|
|
||||||
|
|
||||||
switch (typeof meta.parameter.default) {
|
|
||||||
case 'boolean':
|
|
||||||
isValueDefault =
|
|
||||||
(currentValue === 'true' && meta.parameter.default) ||
|
|
||||||
(currentValue === 'false' && !meta.parameter.default);
|
|
||||||
break;
|
|
||||||
case 'string':
|
|
||||||
isValueDefault = currentValue === meta.parameter.default;
|
|
||||||
break;
|
|
||||||
case 'number':
|
|
||||||
isValueDefault = currentValue === meta.parameter.default.toString();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventData = getExpressionEditorEventsData(meta, isValueDefault);
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
nodeSettings: {
|
|
||||||
valueChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
if (meta.parameterPath !== 'authentication') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventData = getAuthenticationModalEventData(meta);
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
credentialSelected: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const creds = Object.keys(meta.updateInformation.properties.credentials || {});
|
|
||||||
if (creds.length < 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User selected credential from node modal',
|
|
||||||
properties: {
|
|
||||||
credential_name: (meta.updateInformation.properties.credentials as IDataObject)[
|
|
||||||
creds[0]
|
|
||||||
],
|
|
||||||
credential_type: creds[0],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
workflowRun: {
|
|
||||||
runWorkflow: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = getExecutionStartedEventData(meta);
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
runError: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: meta.nodeName
|
|
||||||
? 'Node execution finished'
|
|
||||||
: 'Manual workflow execution finished',
|
|
||||||
properties: {
|
|
||||||
preflight: 'true',
|
|
||||||
status: 'failed',
|
|
||||||
error_message: meta.errorMessages.join('<br /> - '),
|
|
||||||
error_timestamp: new Date(),
|
|
||||||
node_name: meta.nodeName,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
runData: {
|
|
||||||
displayModeChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = getOutputModeChangedEventData(meta);
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
pushConnection: {
|
|
||||||
executionFinished: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = getExecutionFinishedEventData(meta);
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
node: {
|
|
||||||
deleteNode: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = getNodeRemovedEventData(meta);
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
workflow: {
|
|
||||||
activeChange: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: (meta.active && 'User activated workflow') || 'User deactivated workflow',
|
|
||||||
properties: {
|
|
||||||
workflow_id: meta.workflowId,
|
|
||||||
source: 'workflow_modal',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
activeChangeCurrent: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
|
||||||
|
|
||||||
const eventData = {
|
|
||||||
eventName: (meta.active && 'User activated workflow') || 'User deactivated workflow',
|
|
||||||
properties: {
|
|
||||||
source: 'main nav',
|
|
||||||
workflow_id: meta.workflowId,
|
|
||||||
workflow_name: workflowsStore.workflowName,
|
|
||||||
workflow_nodes: workflowsStore.allNodes.map((n) => n.type.split('.')[1]),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
afterUpdate: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User saved workflow',
|
|
||||||
properties: {
|
|
||||||
workflow_id: meta.workflowData.id,
|
|
||||||
workflow_name: meta.workflowData.name,
|
|
||||||
workflow_nodes: meta.workflowData.nodes.map((n) => n.type.split('.')[1]),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
execution: {
|
|
||||||
open: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User opened read-only execution',
|
|
||||||
properties: {
|
|
||||||
workflow_id: meta.workflowId,
|
|
||||||
workflow_name: meta.workflowName,
|
|
||||||
execution_id: meta.executionId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
nodeCreateList: {
|
|
||||||
destroyed: [
|
|
||||||
() => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
if (
|
|
||||||
nodesPanelSession.data.nodeFilter.length > 0 &&
|
|
||||||
nodesPanelSession.data.nodeFilter !== ''
|
|
||||||
) {
|
|
||||||
const eventData = hooksGenerateNodesPanelEvent();
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
selectedTypeChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
const eventData = {
|
|
||||||
eventName: 'User changed nodes panel filter',
|
|
||||||
properties: {
|
|
||||||
old_filter: meta.oldValue,
|
|
||||||
new_filter: meta.newValue,
|
|
||||||
nodes_panel_session_id: nodesPanelSession.pushRef,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
nodesPanelSession.data.filterMode = meta.newValue;
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
},
|
|
||||||
],
|
|
||||||
nodeFilterChanged: [
|
|
||||||
(_, meta) => {
|
|
||||||
const segmentStore = useSegment();
|
|
||||||
if (meta.newValue.length === 0 && nodesPanelSession.data.nodeFilter.length > 0) {
|
|
||||||
const eventData = hooksGenerateNodesPanelEvent();
|
|
||||||
|
|
||||||
segmentStore.track(eventData.eventName, eventData.properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (meta.newValue.length > meta.oldValue.length) {
|
|
||||||
nodesPanelSession.data.nodeFilter = meta.newValue;
|
|
||||||
nodesPanelSession.data.resultsNodes = meta.filteredNodes.map((node) => {
|
|
||||||
if ((node as unknown as INodeUi).name) {
|
|
||||||
return (node as unknown as INodeUi).name.split('.')[1];
|
|
||||||
} else if (node.key) {
|
|
||||||
return node.key.split('.')[1];
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,349 +0,0 @@
|
||||||
import { deepCopy } from 'n8n-workflow';
|
|
||||||
import type {
|
|
||||||
ExecutionError,
|
|
||||||
GenericValue,
|
|
||||||
INodeProperties,
|
|
||||||
ITelemetryTrackProperties,
|
|
||||||
NodeParameterValue,
|
|
||||||
INode,
|
|
||||||
} from 'n8n-workflow';
|
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
|
||||||
import type { TelemetryEventData } from '@/hooks/types';
|
|
||||||
import type { INodeUi } from '@/Interface';
|
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
||||||
import { useRootStore } from '@/stores/root.store';
|
|
||||||
|
|
||||||
export interface UserSavedCredentialsEventData {
|
|
||||||
credential_type: string;
|
|
||||||
credential_id: string;
|
|
||||||
is_new: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getUserSavedCredentialsEventData = (meta: UserSavedCredentialsEventData) => {
|
|
||||||
const rootStore = useRootStore();
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventName: 'User saved credentials',
|
|
||||||
properties: {
|
|
||||||
instance_id: rootStore.instanceId,
|
|
||||||
credential_type: meta.credential_type,
|
|
||||||
credential_id: meta.credential_id,
|
|
||||||
workflow_id: workflowsStore.workflowId,
|
|
||||||
node_type: workflowsStore.activeNode?.name,
|
|
||||||
is_new: meta.is_new,
|
|
||||||
// is_complete: true,
|
|
||||||
// is_valid: true,
|
|
||||||
// error_message: ''
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getOpenWorkflowSettingsEventData = (): TelemetryEventData => {
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventName: 'User opened workflow settings',
|
|
||||||
properties: {
|
|
||||||
workflow_id: workflowsStore.workflowId,
|
|
||||||
workflow_name: workflowsStore.workflowName,
|
|
||||||
current_settings: deepCopy(workflowsStore.workflowSettings),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface UpdatedWorkflowSettingsEventData {
|
|
||||||
oldSettings: Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getUpdatedWorkflowSettingsEventData = (
|
|
||||||
meta: UpdatedWorkflowSettingsEventData,
|
|
||||||
): TelemetryEventData => {
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventName: 'User updated workflow settings',
|
|
||||||
properties: {
|
|
||||||
workflow_id: workflowsStore.workflowId,
|
|
||||||
workflow_name: workflowsStore.workflowName,
|
|
||||||
new_settings: deepCopy(workflowsStore.workflowSettings),
|
|
||||||
old_settings: meta.oldSettings,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface NodeTypeChangedEventData {
|
|
||||||
nodeSubtitle?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getNodeTypeChangedEventData = (meta: NodeTypeChangedEventData): TelemetryEventData => {
|
|
||||||
const store = useNDVStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventName: 'User opened node modal',
|
|
||||||
properties: {
|
|
||||||
node_name: store.activeNode?.name,
|
|
||||||
node_subtitle: meta.nodeSubtitle,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface InsertedItemFromExpEditorEventData {
|
|
||||||
parameter: {
|
|
||||||
displayName: string;
|
|
||||||
};
|
|
||||||
value: string;
|
|
||||||
selectedItem: {
|
|
||||||
variable: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getInsertedItemFromExpEditorEventData = (
|
|
||||||
meta: InsertedItemFromExpEditorEventData,
|
|
||||||
): TelemetryEventData => {
|
|
||||||
const store = useNDVStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventName: 'User inserted item from Expression Editor variable selector',
|
|
||||||
properties: {
|
|
||||||
node_name: store.activeNode?.name,
|
|
||||||
node_type: store.activeNode?.type.split('.')[1],
|
|
||||||
parameter_name: meta.parameter.displayName,
|
|
||||||
variable_expression: meta.selectedItem.variable,
|
|
||||||
} as ITelemetryTrackProperties,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ExpressionEditorEventsData {
|
|
||||||
dialogVisible: boolean;
|
|
||||||
value: string;
|
|
||||||
resolvedExpressionValue: string;
|
|
||||||
parameter: INodeProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getExpressionEditorEventsData = (
|
|
||||||
meta: ExpressionEditorEventsData,
|
|
||||||
isValueDefault: boolean,
|
|
||||||
): TelemetryEventData => {
|
|
||||||
const store = useNDVStore();
|
|
||||||
const eventData: TelemetryEventData = {
|
|
||||||
eventName: '',
|
|
||||||
properties: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!meta.dialogVisible) {
|
|
||||||
eventData.eventName = 'User closed Expression Editor';
|
|
||||||
eventData.properties = {
|
|
||||||
empty_expression: isValueDefault,
|
|
||||||
expression_value: meta.value,
|
|
||||||
expression_result: meta.resolvedExpressionValue.slice(1),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
eventData.eventName = 'User opened Expression Editor';
|
|
||||||
eventData.properties = {
|
|
||||||
node_name: store.activeNode?.name,
|
|
||||||
node_type: store.activeNode?.type.split('.')[1],
|
|
||||||
parameter_name: meta.parameter.displayName,
|
|
||||||
parameter_field_type: meta.parameter.type,
|
|
||||||
new_expression: isValueDefault,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return eventData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface AuthenticationModalEventData {
|
|
||||||
parameterPath: string;
|
|
||||||
oldNodeParameters: Record<string, GenericValue>;
|
|
||||||
parameters: INodeProperties[];
|
|
||||||
newValue: NodeParameterValue;
|
|
||||||
}
|
|
||||||
export const getAuthenticationModalEventData = (
|
|
||||||
meta: AuthenticationModalEventData,
|
|
||||||
): TelemetryEventData => {
|
|
||||||
const store = useNDVStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventName: 'User changed Authentication type from node modal',
|
|
||||||
properties: {
|
|
||||||
node_name: store.activeNode?.name,
|
|
||||||
node_type: store.activeNode?.type.split('.')[1],
|
|
||||||
old_mode:
|
|
||||||
meta.oldNodeParameters.authentication ||
|
|
||||||
(
|
|
||||||
meta.parameters.find((param) => param.name === 'authentication') || {
|
|
||||||
default: 'default',
|
|
||||||
}
|
|
||||||
).default,
|
|
||||||
new_mode: meta.newValue,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface OutputModeChangedEventData {
|
|
||||||
oldValue: string;
|
|
||||||
newValue: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getOutputModeChangedEventData = (
|
|
||||||
meta: OutputModeChangedEventData,
|
|
||||||
): TelemetryEventData => {
|
|
||||||
const store = useNDVStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventName: 'User changed node output view mode',
|
|
||||||
properties: {
|
|
||||||
old_mode: meta.oldValue,
|
|
||||||
new_mode: meta.newValue,
|
|
||||||
node_name: store.activeNode?.name,
|
|
||||||
node_type: store.activeNode?.type.split('.')[1],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ExecutionFinishedEventData {
|
|
||||||
runDataExecutedStartData:
|
|
||||||
| { destinationNode?: string | undefined; runNodeFilter?: string[] | undefined }
|
|
||||||
| undefined;
|
|
||||||
nodeName?: string;
|
|
||||||
errorMessage: string;
|
|
||||||
resultDataError: ExecutionError | undefined;
|
|
||||||
itemsCount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getExecutionFinishedEventData = (
|
|
||||||
meta: ExecutionFinishedEventData,
|
|
||||||
): TelemetryEventData => {
|
|
||||||
const store = useWorkflowsStore();
|
|
||||||
|
|
||||||
const eventData: TelemetryEventData = {
|
|
||||||
eventName: '',
|
|
||||||
properties: {
|
|
||||||
execution_id: store.activeExecutionId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (meta.runDataExecutedStartData?.destinationNode) {
|
|
||||||
eventData.eventName = 'Node execution finished';
|
|
||||||
eventData.properties!.node_type = store.getNodeByName(meta.nodeName || '')?.type.split('.')[1];
|
|
||||||
eventData.properties!.node_name = meta.nodeName;
|
|
||||||
} else {
|
|
||||||
eventData.eventName = 'Manual workflow execution finished';
|
|
||||||
eventData.properties!.workflow_id = store.workflowId;
|
|
||||||
eventData.properties!.workflow_name = store.workflowName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (meta.errorMessage || meta.resultDataError) {
|
|
||||||
eventData.properties!.status = 'failed';
|
|
||||||
eventData.properties!.error_message = meta.resultDataError?.message || '';
|
|
||||||
eventData.properties!.error_stack = meta.resultDataError?.stack || '';
|
|
||||||
eventData.properties!.error_ui_message = meta.errorMessage || '';
|
|
||||||
eventData.properties!.error_timestamp = new Date();
|
|
||||||
|
|
||||||
if (meta.resultDataError && (meta.resultDataError as unknown as { node: INodeUi })?.node) {
|
|
||||||
eventData.properties!.error_node =
|
|
||||||
typeof (meta.resultDataError as unknown as { node: string })?.node === 'string'
|
|
||||||
? (meta.resultDataError as unknown as { node: string })?.node
|
|
||||||
: (meta.resultDataError as unknown as { node: INodeUi })?.node?.name;
|
|
||||||
} else {
|
|
||||||
eventData.properties!.error_node = meta.nodeName;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eventData.properties!.status = 'success';
|
|
||||||
if (meta.runDataExecutedStartData?.destinationNode) {
|
|
||||||
// Node execution finished
|
|
||||||
eventData.properties!.items_count = meta.itemsCount || 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return eventData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface NodeRemovedEventData {
|
|
||||||
node: INodeUi;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getNodeRemovedEventData = (meta: NodeRemovedEventData): TelemetryEventData => {
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
|
||||||
|
|
||||||
return {
|
|
||||||
eventName: 'User removed node from workflow canvas',
|
|
||||||
properties: {
|
|
||||||
node_name: meta.node.name,
|
|
||||||
node_type: meta.node.type,
|
|
||||||
node_disabled: meta.node.disabled,
|
|
||||||
workflow_id: workflowsStore.workflowId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getNodeEditingFinishedEventData = (
|
|
||||||
activeNode: INode | null,
|
|
||||||
): TelemetryEventData | undefined => {
|
|
||||||
switch (activeNode?.type) {
|
|
||||||
case 'n8n-nodes-base.httpRequest':
|
|
||||||
const domain = (activeNode.parameters.url as string).split('/')[2];
|
|
||||||
return {
|
|
||||||
eventName: 'User finished httpRequest node editing',
|
|
||||||
properties: {
|
|
||||||
method: activeNode.parameters.method,
|
|
||||||
domain,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
case 'n8n-nodes-base.function':
|
|
||||||
return {
|
|
||||||
eventName: 'User finished function node editing',
|
|
||||||
properties: {
|
|
||||||
node_name: activeNode.name,
|
|
||||||
code: activeNode.parameters.functionCode,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
case 'n8n-nodes-base.functionItem':
|
|
||||||
return {
|
|
||||||
eventName: 'User finished functionItem node editing',
|
|
||||||
properties: {
|
|
||||||
node_name: activeNode.name,
|
|
||||||
code: activeNode.parameters.functionCode,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface ExecutionStartedEventData {
|
|
||||||
nodeName?: string;
|
|
||||||
source?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getExecutionStartedEventData = (
|
|
||||||
meta: ExecutionStartedEventData,
|
|
||||||
): TelemetryEventData => {
|
|
||||||
const store = useWorkflowsStore();
|
|
||||||
|
|
||||||
const eventData: TelemetryEventData = {
|
|
||||||
eventName: '',
|
|
||||||
properties: {
|
|
||||||
execution_id: store.activeExecutionId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// node execution
|
|
||||||
if (meta.nodeName) {
|
|
||||||
eventData.eventName = 'User started node execution';
|
|
||||||
eventData.properties!.source = 'unknown';
|
|
||||||
eventData.properties!.node_type = store.getNodeByName(meta.nodeName)?.type.split('.')[1];
|
|
||||||
eventData.properties!.node_name = meta.nodeName;
|
|
||||||
|
|
||||||
if (meta.source === 'RunData.ExecuteNodeButton') {
|
|
||||||
eventData.properties!.source = 'node_modal';
|
|
||||||
} else if (meta.source === 'Node.executeNode') {
|
|
||||||
eventData.properties!.source = 'workflow_canvas';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// workflow execution
|
|
||||||
eventData.eventName = 'User started manual workflow execution';
|
|
||||||
eventData.properties!.workflow_id = store.workflowId;
|
|
||||||
eventData.properties!.workflow_name = store.workflowName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return eventData;
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
export * from './getters';
|
|
|
@ -1,143 +0,0 @@
|
||||||
import {
|
|
||||||
CODE_NODE_TYPE,
|
|
||||||
HTTP_REQUEST_NODE_TYPE,
|
|
||||||
MANUAL_TRIGGER_NODE_TYPE,
|
|
||||||
SCHEDULE_TRIGGER_NODE_TYPE,
|
|
||||||
SET_NODE_TYPE,
|
|
||||||
WEBHOOK_NODE_TYPE,
|
|
||||||
} from '@/constants';
|
|
||||||
import { defineStore } from 'pinia';
|
|
||||||
|
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
|
||||||
import type { INodeTypeDescription, IRun, ITelemetryTrackProperties } from 'n8n-workflow';
|
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
|
||||||
import { useUsersStore } from '@/stores/users.store';
|
|
||||||
|
|
||||||
const EVENTS = {
|
|
||||||
ADDED_MANUAL_TRIGGER: 'User added manual trigger',
|
|
||||||
ADDED_SCHEDULE_TRIGGER: 'User added schedule trigger',
|
|
||||||
ADDED_DATA_TRIGGER: 'User added data trigger',
|
|
||||||
RECEIEVED_MULTIPLE_DATA_ITEMS: 'User received multiple data items',
|
|
||||||
EXECUTED_MANUAL_TRIGGER: 'User executed manual trigger successfully',
|
|
||||||
EXECUTED_SCHEDULE_TRIGGER: 'User executed schedule trigger successfully',
|
|
||||||
EXECUTED_DATA_NODE_TRIGGER: 'User executed data node successfully',
|
|
||||||
MAPPED_DATA: 'User mapped data',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useSegment = defineStore('segment', () => {
|
|
||||||
const nodeTypesStore = useNodeTypesStore();
|
|
||||||
const workflowsStore = useWorkflowsStore();
|
|
||||||
const settingsStore = useSettingsStore();
|
|
||||||
const usersStore = useUsersStore();
|
|
||||||
|
|
||||||
const track = (eventName: string, properties?: ITelemetryTrackProperties) => {
|
|
||||||
if (settingsStore.telemetry.enabled) {
|
|
||||||
window.analytics?.track(eventName, properties);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const page = (category: string, name: string, properties?: ITelemetryTrackProperties) => {
|
|
||||||
if (settingsStore.telemetry.enabled) {
|
|
||||||
window.analytics?.page(category, name, properties);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const identify = () => {
|
|
||||||
const userId = usersStore.currentUserId;
|
|
||||||
|
|
||||||
if (settingsStore.telemetry.enabled && userId) {
|
|
||||||
window.analytics?.identify(userId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const trackAddedTrigger = (nodeTypeName: string) => {
|
|
||||||
if (!nodeTypesStore.isTriggerNode(nodeTypeName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nodeTypeName === MANUAL_TRIGGER_NODE_TYPE) {
|
|
||||||
track(EVENTS.ADDED_MANUAL_TRIGGER);
|
|
||||||
} else if (nodeTypeName === SCHEDULE_TRIGGER_NODE_TYPE) {
|
|
||||||
track(EVENTS.ADDED_SCHEDULE_TRIGGER);
|
|
||||||
} else {
|
|
||||||
track(EVENTS.ADDED_DATA_TRIGGER);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const trackSuccessfulWorkflowExecution = (runData: IRun) => {
|
|
||||||
const dataNodeTypes: Set<string> = new Set<string>();
|
|
||||||
const multipleOutputNodes: Set<string> = new Set<string>();
|
|
||||||
let hasManualTrigger = false;
|
|
||||||
let hasScheduleTrigger = false;
|
|
||||||
for (const nodeName of Object.keys(runData.data.resultData.runData)) {
|
|
||||||
const nodeRunData = runData.data.resultData.runData[nodeName];
|
|
||||||
const node = workflowsStore.getNodeByName(nodeName);
|
|
||||||
const nodeTypeName = node ? node.type : 'unknown';
|
|
||||||
if (
|
|
||||||
nodeRunData[0].data?.main &&
|
|
||||||
nodeRunData[0].data.main.some((out) => out && out?.length > 1)
|
|
||||||
) {
|
|
||||||
multipleOutputNodes.add(nodeTypeName);
|
|
||||||
}
|
|
||||||
if (node && !node.disabled) {
|
|
||||||
const nodeType = nodeTypesStore.getNodeType(node.type, node.typeVersion);
|
|
||||||
if (isDataNodeType(nodeType)) {
|
|
||||||
dataNodeTypes.add(nodeTypeName);
|
|
||||||
}
|
|
||||||
if (isManualTriggerNode(nodeType)) {
|
|
||||||
hasManualTrigger = true;
|
|
||||||
}
|
|
||||||
if (isScheduleTriggerNode(nodeType)) {
|
|
||||||
hasScheduleTrigger = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (multipleOutputNodes.size > 0) {
|
|
||||||
track(EVENTS.RECEIEVED_MULTIPLE_DATA_ITEMS, {
|
|
||||||
nodeTypes: Array.from(multipleOutputNodes),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (dataNodeTypes.size > 0) {
|
|
||||||
track(EVENTS.EXECUTED_DATA_NODE_TRIGGER, {
|
|
||||||
nodeTypes: Array.from(dataNodeTypes),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (hasManualTrigger) {
|
|
||||||
track(EVENTS.EXECUTED_MANUAL_TRIGGER);
|
|
||||||
}
|
|
||||||
if (hasScheduleTrigger) {
|
|
||||||
track(EVENTS.EXECUTED_SCHEDULE_TRIGGER);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const isManualTriggerNode = (nodeType: INodeTypeDescription | null): boolean => {
|
|
||||||
return !!nodeType && nodeType.name === MANUAL_TRIGGER_NODE_TYPE;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isScheduleTriggerNode = (nodeType: INodeTypeDescription | null): boolean => {
|
|
||||||
return !!nodeType && nodeType.name === SCHEDULE_TRIGGER_NODE_TYPE;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isDataNodeType = (nodeType: INodeTypeDescription | null): boolean => {
|
|
||||||
if (!nodeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const includeCoreNodes = [
|
|
||||||
HTTP_REQUEST_NODE_TYPE,
|
|
||||||
CODE_NODE_TYPE,
|
|
||||||
SET_NODE_TYPE,
|
|
||||||
WEBHOOK_NODE_TYPE,
|
|
||||||
];
|
|
||||||
return !nodeTypesStore.isCoreNodeType(nodeType) || includeCoreNodes.includes(nodeType.name);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
track,
|
|
||||||
trackAddedTrigger,
|
|
||||||
trackSuccessfulWorkflowExecution,
|
|
||||||
identify,
|
|
||||||
page,
|
|
||||||
EVENTS,
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,24 +1,15 @@
|
||||||
import type { N8nInput } from 'n8n-design-system';
|
import type { N8nInput } from 'n8n-design-system';
|
||||||
import type {
|
import type {
|
||||||
|
ExecutionError,
|
||||||
|
GenericValue,
|
||||||
IConnections,
|
IConnections,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
INodeTypeDescription,
|
INodeTypeDescription,
|
||||||
ITelemetryTrackProperties,
|
ITelemetryTrackProperties,
|
||||||
|
NodeParameterValue,
|
||||||
NodeParameterValueType,
|
NodeParameterValueType,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import type { RouteLocation } from 'vue-router';
|
import type { RouteLocation } from 'vue-router';
|
||||||
import type {
|
|
||||||
AuthenticationModalEventData,
|
|
||||||
ExecutionFinishedEventData,
|
|
||||||
ExecutionStartedEventData,
|
|
||||||
ExpressionEditorEventsData,
|
|
||||||
InsertedItemFromExpEditorEventData,
|
|
||||||
NodeRemovedEventData,
|
|
||||||
NodeTypeChangedEventData,
|
|
||||||
OutputModeChangedEventData,
|
|
||||||
UpdatedWorkflowSettingsEventData,
|
|
||||||
UserSavedCredentialsEventData,
|
|
||||||
} from '@/hooks/segment';
|
|
||||||
import type {
|
import type {
|
||||||
INodeCreateElement,
|
INodeCreateElement,
|
||||||
INodeUi,
|
INodeUi,
|
||||||
|
@ -40,6 +31,62 @@ export interface ExternalHooksGenericContext {
|
||||||
[key: string]: ExternalHooksMethod[];
|
[key: string]: ExternalHooksMethod[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UserSavedCredentialsEventData {
|
||||||
|
credential_type: string;
|
||||||
|
credential_id: string;
|
||||||
|
is_new: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdatedWorkflowSettingsEventData {
|
||||||
|
oldSettings: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NodeTypeChangedEventData {
|
||||||
|
nodeSubtitle?: string;
|
||||||
|
}
|
||||||
|
interface InsertedItemFromExpEditorEventData {
|
||||||
|
parameter: {
|
||||||
|
displayName: string;
|
||||||
|
};
|
||||||
|
value: string;
|
||||||
|
selectedItem: {
|
||||||
|
variable: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
interface ExpressionEditorEventsData {
|
||||||
|
dialogVisible: boolean;
|
||||||
|
value: string;
|
||||||
|
resolvedExpressionValue: string;
|
||||||
|
parameter: INodeProperties;
|
||||||
|
}
|
||||||
|
interface AuthenticationModalEventData {
|
||||||
|
parameterPath: string;
|
||||||
|
oldNodeParameters: Record<string, GenericValue>;
|
||||||
|
parameters: INodeProperties[];
|
||||||
|
newValue: NodeParameterValue;
|
||||||
|
}
|
||||||
|
interface OutputModeChangedEventData {
|
||||||
|
oldValue: string;
|
||||||
|
newValue: string;
|
||||||
|
}
|
||||||
|
interface ExecutionFinishedEventData {
|
||||||
|
runDataExecutedStartData:
|
||||||
|
| { destinationNode?: string | undefined; runNodeFilter?: string[] | undefined }
|
||||||
|
| undefined;
|
||||||
|
nodeName?: string;
|
||||||
|
errorMessage: string;
|
||||||
|
resultDataError: ExecutionError | undefined;
|
||||||
|
itemsCount: number;
|
||||||
|
}
|
||||||
|
interface NodeRemovedEventData {
|
||||||
|
node: INodeUi;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExecutionStartedEventData {
|
||||||
|
nodeName?: string;
|
||||||
|
source?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ExternalHooks {
|
export interface ExternalHooks {
|
||||||
parameterInput: {
|
parameterInput: {
|
||||||
mount: Array<
|
mount: Array<
|
||||||
|
|
|
@ -332,7 +332,6 @@ import { useNodeCreatorStore } from '@/stores/nodeCreator.store';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import { usePushConnectionStore } from '@/stores/pushConnection.store';
|
import { usePushConnectionStore } from '@/stores/pushConnection.store';
|
||||||
import { useRootStore } from '@/stores/root.store';
|
import { useRootStore } from '@/stores/root.store';
|
||||||
import { useSegment } from '@/stores/segment.store';
|
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import { useTagsStore } from '@/stores/tags.store';
|
import { useTagsStore } from '@/stores/tags.store';
|
||||||
import { useTemplatesStore } from '@/stores/templates.store';
|
import { useTemplatesStore } from '@/stores/templates.store';
|
||||||
|
@ -2697,7 +2696,6 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
void this.externalHooks.run('nodeView.addNodeButton', { nodeTypeName });
|
void this.externalHooks.run('nodeView.addNodeButton', { nodeTypeName });
|
||||||
useSegment().trackAddedTrigger(nodeTypeName);
|
|
||||||
const trackProperties: ITelemetryTrackProperties = {
|
const trackProperties: ITelemetryTrackProperties = {
|
||||||
node_type: nodeTypeName,
|
node_type: nodeTypeName,
|
||||||
node_version: newNodeData.typeVersion,
|
node_version: newNodeData.typeVersion,
|
||||||
|
|
Loading…
Reference in a new issue