Support telemetry page (#2756)

* support telemetry page events

* add log level to FE settings

* add debug logging

* fix types

* state fix

* move call
This commit is contained in:
Mutasem Aldmour 2022-02-04 00:24:01 +02:00 committed by GitHub
parent 0bf554394a
commit 7bdb7e2a25
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 51 additions and 23 deletions

View file

@ -409,6 +409,7 @@ export interface IN8nUISettings {
telemetry: ITelemetrySettings; telemetry: ITelemetrySettings;
personalizationSurvey: IPersonalizationSurvey; personalizationSurvey: IPersonalizationSurvey;
defaultLocale: string; defaultLocale: string;
logLevel: 'info' | 'debug' | 'warn' | 'error' | 'verbose';
} }
export interface IPersonalizationSurveyAnswers { export interface IPersonalizationSurveyAnswers {

View file

@ -288,6 +288,7 @@ class App {
shouldShow: false, shouldShow: false,
}, },
defaultLocale: config.get('defaultLocale'), defaultLocale: config.get('defaultLocale'),
logLevel: config.get('logs.level'),
}; };
} }

View file

@ -22,6 +22,9 @@ export default Vue.extend({
components: { components: {
Telemetry, Telemetry,
}, },
mounted() {
this.$telemetry.page('Editor', this.$route.name);
},
watch: { watch: {
'$route'(route) { '$route'(route) {
this.$telemetry.page('Editor', route.name); this.$telemetry.page('Editor', route.name);

View file

@ -536,6 +536,7 @@ export interface IN8nUISettings {
personalizationSurvey?: IPersonalizationSurvey; personalizationSurvey?: IPersonalizationSurvey;
telemetry: ITelemetrySettings; telemetry: ITelemetrySettings;
defaultLocale: string; defaultLocale: string;
logLevel: ILogLevel;
} }
export interface IWorkflowSettings extends IWorkflowSettingsWorkflow { export interface IWorkflowSettings extends IWorkflowSettingsWorkflow {
@ -680,7 +681,6 @@ export interface IRootState {
workflow: IWorkflowDb; workflow: IWorkflowDb;
sidebarMenuItems: IMenuItem[]; sidebarMenuItems: IMenuItem[];
instanceId: string; instanceId: string;
telemetry: ITelemetrySettings | null;
} }
export interface ICredentialTypeMap { export interface ICredentialTypeMap {
@ -718,6 +718,8 @@ export interface IUiState {
isPageLoading: boolean; isPageLoading: boolean;
} }
export type ILogLevel = 'info' | 'debug' | 'warn' | 'error' | 'verbose';
export interface ISettingsState { export interface ISettingsState {
settings: IN8nUISettings; settings: IN8nUISettings;
promptsData: IN8nPrompts; promptsData: IN8nPrompts;

View file

@ -10,12 +10,12 @@ import { mapGetters } from 'vuex';
export default Vue.extend({ export default Vue.extend({
name: 'Telemetry', name: 'Telemetry',
computed: { computed: {
...mapGetters(['telemetry']), ...mapGetters('settings', ['telemetry']),
}, },
watch: { watch: {
telemetry(opts) { telemetry(opts) {
if (opts.enabled) { if (opts && opts.enabled) {
this.$telemetry.init(opts, this.$store.getters.instanceId); this.$telemetry.init(opts, this.$store.getters.instanceId, this.$store.getters['settings/logLevel']);
} }
}, },
}, },

View file

@ -1,5 +1,6 @@
import { ActionContext, Module } from 'vuex'; import { ActionContext, Module } from 'vuex';
import { import {
ILogLevel,
IN8nPrompts, IN8nPrompts,
IN8nUISettings, IN8nUISettings,
IN8nValueSurveyData, IN8nValueSurveyData,
@ -11,6 +12,7 @@ import { getPromptsData, getSettings, submitValueSurvey, submitPersonalizationSu
import Vue from 'vue'; import Vue from 'vue';
import { getPersonalizedNodeTypes } from './helper'; import { getPersonalizedNodeTypes } from './helper';
import { CONTACT_PROMPT_MODAL_KEY, PERSONALIZATION_MODAL_KEY, VALUE_SURVEY_MODAL_KEY } from '@/constants'; import { CONTACT_PROMPT_MODAL_KEY, PERSONALIZATION_MODAL_KEY, VALUE_SURVEY_MODAL_KEY } from '@/constants';
import { ITelemetrySettings } from 'n8n-workflow';
const module: Module<ISettingsState, IRootState> = { const module: Module<ISettingsState, IRootState> = {
namespaced: true, namespaced: true,
@ -30,6 +32,15 @@ const module: Module<ISettingsState, IRootState> = {
getPromptsData(state: ISettingsState) { getPromptsData(state: ISettingsState) {
return state.promptsData; return state.promptsData;
}, },
telemetry: (state): ITelemetrySettings => {
return state.settings.telemetry;
},
logLevel: (state): ILogLevel => {
return state.settings.logLevel;
},
isTelemetryEnabled: (state) => {
return state.settings.telemetry && state.settings.telemetry.enabled;
},
}, },
mutations: { mutations: {
setSettings(state: ISettingsState, settings: IN8nUISettings) { setSettings(state: ISettingsState, settings: IN8nUISettings) {
@ -66,7 +77,6 @@ const module: Module<ISettingsState, IRootState> = {
context.commit('setN8nMetadata', settings.n8nMetadata || {}, {root: true}); context.commit('setN8nMetadata', settings.n8nMetadata || {}, {root: true});
context.commit('setDefaultLocale', settings.defaultLocale, {root: true}); context.commit('setDefaultLocale', settings.defaultLocale, {root: true});
context.commit('versions/setVersionNotificationSettings', settings.versionNotifications, {root: true}); context.commit('versions/setVersionNotificationSettings', settings.versionNotifications, {root: true});
context.commit('setTelemetry', settings.telemetry, {root: true});
const showPersonalizationsModal = settings.personalizationSurvey && settings.personalizationSurvey.shouldShow && !settings.personalizationSurvey.answers; const showPersonalizationsModal = settings.personalizationSurvey && settings.personalizationSurvey.shouldShow && !settings.personalizationSurvey.answers;
@ -81,7 +91,7 @@ const module: Module<ISettingsState, IRootState> = {
context.commit('setPersonalizationAnswers', results); context.commit('setPersonalizationAnswers', results);
}, },
async fetchPromptsData(context: ActionContext<ISettingsState, IRootState>) { async fetchPromptsData(context: ActionContext<ISettingsState, IRootState>) {
if (!context.rootGetters.isTelemetryEnabled) { if (!context.getters.isTelemetryEnabled) {
return; return;
} }

View file

@ -3,7 +3,7 @@ import {
ITelemetrySettings, ITelemetrySettings,
IDataObject, IDataObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { INodeCreateElement } from "@/Interface"; import { ILogLevel, INodeCreateElement } from "@/Interface";
declare module 'vue/types/vue' { declare module 'vue/types/vue' {
interface Vue { interface Vue {
@ -35,6 +35,8 @@ interface IUserNodesPanelSession {
class Telemetry { class Telemetry {
private pageEventQueue: Array<{category?: string, name?: string | null}>;
private get telemetry() { private get telemetry() {
// @ts-ignore // @ts-ignore
return window.rudderanalytics; return window.rudderanalytics;
@ -49,14 +51,20 @@ class Telemetry {
}, },
}; };
init(options: ITelemetrySettings, instanceId: string) { constructor() {
this.pageEventQueue = [];
}
init(options: ITelemetrySettings, instanceId: string, logLevel?: ILogLevel) {
if (options.enabled && !this.telemetry) { if (options.enabled && !this.telemetry) {
if(!options.config) { if(!options.config) {
return; return;
} }
this.loadTelemetryLibrary(options.config.key, options.config.url, { integrations: { All: false }, loadIntegration: false }); const logging = logLevel === 'debug' ? { logLevel: 'DEBUG'} : {};
this.loadTelemetryLibrary(options.config.key, options.config.url, { integrations: { All: false }, loadIntegration: false, ...logging});
this.telemetry.identify(instanceId); this.telemetry.identify(instanceId);
this.flushPageEvents();
} }
} }
@ -66,10 +74,26 @@ class Telemetry {
} }
} }
page(category?: string, name?: string | undefined | null) { page(category?: string, name?: string | null) {
if (this.telemetry) { if (this.telemetry) {
this.telemetry.page(category, name); this.telemetry.page(category, name);
} }
else {
this.pageEventQueue.push({
category,
name,
});
}
}
flushPageEvents() {
const queue = this.pageEventQueue;
this.pageEventQueue = [];
queue.forEach(({category, name}) => {
if (this.telemetry) {
this.telemetry.page(category, name);
}
});
} }
trackNodesPanel(event: string, properties: IDataObject = {}) { trackNodesPanel(event: string, properties: IDataObject = {}) {

View file

@ -12,7 +12,6 @@ import {
INodeIssueData, INodeIssueData,
INodeTypeDescription, INodeTypeDescription,
IRunData, IRunData,
ITelemetrySettings,
ITaskData, ITaskData,
IWorkflowSettings, IWorkflowSettings,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -89,7 +88,6 @@ const state: IRootState = {
}, },
sidebarMenuItems: [], sidebarMenuItems: [],
instanceId: '', instanceId: '',
telemetry: null,
}; };
const modules = { const modules = {
@ -545,9 +543,6 @@ export const store = new Vuex.Store({
setInstanceId(state, instanceId: string) { setInstanceId(state, instanceId: string) {
Vue.set(state, 'instanceId', instanceId); Vue.set(state, 'instanceId', instanceId);
}, },
setTelemetry(state, telemetry: ITelemetrySettings) {
Vue.set(state, 'telemetry', telemetry);
},
setOauthCallbackUrls(state, urls: IDataObject) { setOauthCallbackUrls(state, urls: IDataObject) {
Vue.set(state, 'oauthCallbackUrls', urls); Vue.set(state, 'oauthCallbackUrls', urls);
}, },
@ -659,10 +654,6 @@ export const store = new Vuex.Store({
return state.workflow.id === PLACEHOLDER_EMPTY_WORKFLOW_ID; return state.workflow.id === PLACEHOLDER_EMPTY_WORKFLOW_ID;
}, },
isTelemetryEnabled: (state) => {
return state.telemetry && state.telemetry.enabled;
},
currentWorkflowHasWebhookNode: (state: IRootState): boolean => { currentWorkflowHasWebhookNode: (state: IRootState): boolean => {
return !!state.workflow.nodes.find((node: INodeUi) => !!node.webhookId); return !!state.workflow.nodes.find((node: INodeUi) => !!node.webhookId);
}, },
@ -730,9 +721,6 @@ export const store = new Vuex.Store({
versionCli: (state): string => { versionCli: (state): string => {
return state.versionCli; return state.versionCli;
}, },
telemetry: (state): ITelemetrySettings | null => {
return state.telemetry;
},
oauthCallbackUrls: (state): object => { oauthCallbackUrls: (state): object => {
return state.oauthCallbackUrls; return state.oauthCallbackUrls;
}, },

View file

@ -2752,7 +2752,6 @@ export default mixins(
}); });
this.$externalHooks().run('nodeView.mount'); this.$externalHooks().run('nodeView.mount');
this.$telemetry.page('Editor', this.$route.name);
}, },
destroyed () { destroyed () {