diff --git a/packages/insomnia-app/app/common/__mocks__/analytics.ts b/packages/insomnia-app/app/common/__mocks__/analytics.ts index 399ed08ba..f75370ed7 100644 --- a/packages/insomnia-app/app/common/__mocks__/analytics.ts +++ b/packages/insomnia-app/app/common/__mocks__/analytics.ts @@ -1,5 +1,4 @@ // WARNING: changing this to `export default` will break the mock and be incredibly hard to debug. Ask me how I know. const _analytics = jest.requireActual('../analytics'); -_analytics.trackEvent = jest.fn(); _analytics.trackSegmentEvent = jest.fn(); module.exports = _analytics; diff --git a/packages/insomnia-app/app/common/__tests__/analytics.test.ts b/packages/insomnia-app/app/common/__tests__/analytics.test.ts deleted file mode 100644 index 488f701ce..000000000 --- a/packages/insomnia-app/app/common/__tests__/analytics.test.ts +++ /dev/null @@ -1,203 +0,0 @@ -import * as electron from 'electron'; -import { EventEmitter } from 'events'; - -import { globalBeforeEach } from '../../__jest__/before-each'; -import * as models from '../../models/index'; -import { _trackEvent, _trackPageView } from '../analytics'; -import { - getAppId, - getAppName, - getAppPlatform, - getAppVersion, - getBrowserUserAgent, - getGoogleAnalyticsId, - getGoogleAnalyticsLocation, -} from '../constants'; - -describe('init()', () => { - beforeEach(async () => { - await globalBeforeEach(); - electron.net.request = jest.fn(() => { - const req = new EventEmitter(); - - req.end = function() {}; - - return req; - }); - jest.useFakeTimers(); - }); - - it('does not work with tracking disabled', async () => { - const settings = await models.settings.patch({ - enableAnalytics: false, - deviceId: 'device', - }); - expect(settings.enableAnalytics).toBe(false); - expect(electron.net.request.mock.calls).toEqual([]); - await _trackEvent({ interactive: true, category: 'Foo', action: 'Bar' }); - jest.runAllTimers(); - expect(electron.net.request.mock.calls).toEqual([]); - }); - - it('works with tracking enabled', async () => { - const settings = await models.settings.patch({ - enableAnalytics: true, - deviceId: 'device', - }); - expect(settings.enableAnalytics).toBe(true); - expect(electron.net.request.mock.calls).toEqual([]); - await _trackEvent({ interactive: true, category: 'Foo', action: 'Bar' }); - jest.runAllTimers(); - expect(electron.net.request.mock.calls).toEqual([ - [ - 'https://www.google-analytics.com/collect?' + - 'v=1&' + - `tid=${getGoogleAnalyticsId()}&` + - 'cid=device&' + - `ua=${getBrowserUserAgent()}&` + - `dl=${encodeURIComponent(getGoogleAnalyticsLocation())}%2F&` + - 'sr=1920x1080&' + - 'ul=en-US&' + - `dt=${getAppId()}%3A${getAppVersion()}&` + - `cd1=${getAppPlatform()}&` + - `cd2=${getAppVersion()}&` + - 'aip=1&' + - `an=${encodeURI(getAppName())}&` + - `aid=${getAppId()}&` + - `av=${getAppVersion()}&` + - 'vp=1900x1060&' + - 'de=UTF-8&' + - 't=event&' + - 'ec=Foo&' + - 'ea=Bar', - ], - ]); - }); - - it('tracks non-interactive event', async () => { - await models.settings.patch({ - deviceId: 'device', - enableAnalytics: true, - }); - await _trackEvent({ interactive: false, category: 'Foo', action: 'Bar' }); - jest.runAllTimers(); - expect(electron.net.request.mock.calls).toEqual([ - [ - 'https://www.google-analytics.com/collect?' + - 'v=1&' + - `tid=${getGoogleAnalyticsId()}&` + - 'cid=device&' + - `ua=${getBrowserUserAgent()}&` + - `dl=${encodeURIComponent(getGoogleAnalyticsLocation())}%2F&` + - 'sr=1920x1080&' + - 'ul=en-US&' + - `dt=${getAppId()}%3A${getAppVersion()}&` + - `cd1=${getAppPlatform()}&` + - `cd2=${getAppVersion()}&` + - 'aip=1&' + - `an=${encodeURI(getAppName())}&` + - `aid=${getAppId()}&` + - `av=${getAppVersion()}&` + - 'vp=1900x1060&' + - 'de=UTF-8&' + - 't=event&' + - 'ec=Foo&' + - 'ea=Bar&' + - 'ni=1', - ], - ]); - }); - - it('tracks page view', async () => { - await models.settings.patch({ - deviceId: 'device', - enableAnalytics: true, - }); - await _trackPageView('/my/path'); - jest.runAllTimers(); - expect(electron.net.request.mock.calls).toEqual([ - [ - 'https://www.google-analytics.com/collect?' + - 'v=1&' + - `tid=${getGoogleAnalyticsId()}&` + - 'cid=device&' + - `ua=${getBrowserUserAgent()}&` + - `dl=${encodeURIComponent(getGoogleAnalyticsLocation())}%2Fmy%2Fpath&` + - 'sr=1920x1080&' + - 'ul=en-US&' + - `dt=${getAppId()}%3A${getAppVersion()}&` + - `cd1=${getAppPlatform()}&` + - `cd2=${getAppVersion()}&` + - 'aip=1&' + - `an=${encodeURI(getAppName())}&` + - `aid=${getAppId()}&` + - `av=${getAppVersion()}&` + - 'vp=1900x1060&' + - 'de=UTF-8&' + - 't=pageview', - ], - ]); - }); - - it('tracking page view remembers path', async () => { - await models.settings.patch({ - deviceId: 'device', - enableAnalytics: true, - }); - await _trackPageView('/my/path'); - jest.runAllTimers(); - await _trackEvent({ - interactive: true, - category: 'cat', - action: 'act', - label: 'lab', - value: 'val', - }); - expect(electron.net.request.mock.calls).toEqual([ - [ - 'https://www.google-analytics.com/collect?' + - 'v=1&' + - `tid=${getGoogleAnalyticsId()}&` + - 'cid=device&' + - `ua=${getBrowserUserAgent()}&` + - `dl=${encodeURIComponent(getGoogleAnalyticsLocation())}%2Fmy%2Fpath&` + - 'sr=1920x1080&' + - 'ul=en-US&' + - `dt=${getAppId()}%3A${getAppVersion()}&` + - `cd1=${getAppPlatform()}&` + - `cd2=${getAppVersion()}&` + - 'aip=1&' + - `an=${encodeURI(getAppName())}&` + - `aid=${getAppId()}&` + - `av=${getAppVersion()}&` + - 'vp=1900x1060&' + - 'de=UTF-8&' + - 't=pageview', - ], - [ - 'https://www.google-analytics.com/collect?' + - 'v=1&' + - `tid=${getGoogleAnalyticsId()}&` + - 'cid=device&' + - `ua=${getBrowserUserAgent()}&` + - `dl=${encodeURIComponent(getGoogleAnalyticsLocation())}%2Fmy%2Fpath&` + - 'sr=1920x1080&' + - 'ul=en-US&' + - `dt=${getAppId()}%3A${getAppVersion()}&` + - `cd1=${getAppPlatform()}&` + - `cd2=${getAppVersion()}&` + - 'aip=1&' + - `an=${encodeURI(getAppName())}&` + - `aid=${getAppId()}&` + - `av=${getAppVersion()}&` + - 'vp=1900x1060&' + - 'de=UTF-8&' + - 't=event&' + - 'ec=cat&' + - 'ea=act&' + - 'el=lab&' + - 'ev=val', - ], - ]); - }); -}); diff --git a/packages/insomnia-app/app/common/analytics.ts b/packages/insomnia-app/app/common/analytics.ts index a9253b971..f9ca95dc5 100644 --- a/packages/insomnia-app/app/common/analytics.ts +++ b/packages/insomnia-app/app/common/analytics.ts @@ -1,95 +1,58 @@ import Analytics from 'analytics-node'; -import * as electron from 'electron'; -import { buildQueryStringFromParams, joinUrlAndQueryString } from 'insomnia-url'; import * as uuid from 'uuid'; import { getAccountId } from '../account/session'; import { database as db } from '../common/database'; import * as models from '../models/index'; -import type { RequestParameter } from '../models/request'; import { isSettings } from '../models/settings'; import { - getAppId, getAppName, getAppPlatform, getAppVersion, - getGoogleAnalyticsId, - getGoogleAnalyticsLocation, getSegmentWriteKey, - isDevelopment, } from './constants'; -import { getScreenResolution, getUserLanguage, getViewportSize } from './electron-helpers'; -const DIMENSION_PLATFORM = 1; -const DIMENSION_VERSION = 2; -const KEY_TRACKING_ID = 'tid'; -const KEY_VERSION = 'v'; -const KEY_CLIENT_ID = 'cid'; -const KEY_HIT_TYPE = 't'; -const KEY_LOCATION = 'dl'; -const KEY_TITLE = 'dt'; -const KEY_NON_INTERACTION = 'ni'; -const KEY_VIEWPORT_SIZE = 'vp'; -const KEY_SCREEN_RESOLUTION = 'sr'; -const KEY_USER_LANGUAGE = 'ul'; -const KEY_USER_AGENT = 'ua'; -const KEY_DOCUMENT_ENCODING = 'de'; -const KEY_EVENT_CATEGORY = 'ec'; -const KEY_EVENT_ACTION = 'ea'; -const KEY_EVENT_LABEL = 'el'; -const KEY_EVENT_VALUE = 'ev'; -const KEY_ANONYMIZE_IP = 'aip'; -const KEY_APPLICATION_NAME = 'an'; -const KEY_APPLICATION_ID = 'aid'; -const KEY_APPLICATION_VERSION = 'av'; -const KEY_CUSTOM_DIMENSION_PREFIX = 'cd'; -let _currentLocationPath = '/'; +const segmentClient = new Analytics(getSegmentWriteKey(), { + // @ts-expect-error -- TSCONVERSION + axiosConfig: { + // This is needed to ensure that we use the NodeJS adapter in the render process + ...(global?.require && { + adapter: global.require('axios/lib/adapters/http'), + }), + }, +}); -export function trackEvent( - category: string, - action: string, - label?: string | null, - value?: string | null, -) { - process.nextTick(async () => { - await _trackEvent({ - interactive: true, - category, - action, - label, - value, - }); - }); -} - -export function trackPageView(path: string) { - process.nextTick(async () => { - await _trackPageView(path); - }); -} - -export async function getDeviceId() { +const getDeviceId = async () => { const settings = await models.settings.getOrCreate(); - let { deviceId } = settings; + return settings.deviceId || (await models.settings.update(settings, { deviceId: uuid.v4() })).deviceId; +}; - if (!deviceId) { - // Migrate old GA ID into settings model if needed - const oldId = (window && window.localStorage.getItem('gaClientId')) || null; - deviceId = oldId || uuid.v4(); - await models.settings.update(settings, { - deviceId, +const sendSegment = async (segmentType: 'track' | 'page', options) => { + try { + const anonymousId = await getDeviceId(); + const userId = getAccountId(); + const context = { + app: { name: getAppName(), version: getAppVersion() }, + os: { name: _getOsName(), version: process.getSystemVersion() }, + }; + segmentClient?.[segmentType]({ ...options, context, anonymousId, userId }, error => { + if (error) console.warn('[analytics] Error sending segment event', error); }); + } catch (error: unknown) { + console.warn('[analytics] Unexpected error while sending segment event', error); } - - return deviceId; -} - -let segmentClient: Analytics | null = null; +}; export enum SegmentEvent { appStarted = 'App Started', collectionCreate = 'Collection Created', + criticalError = 'Critical Error Encountered', + dataExport = 'Data Exported', + dataImport = 'Data Imported', documentCreate = 'Document Created', + kongConnected = 'Kong Connected', + kongSync = 'Kong Synced', + requestBodyTypeSelect = 'Request Body Type Selected', requestCreate = 'Request Created', requestExecute = 'Request Executed', projectLocalCreate = 'Local Project Created', @@ -110,9 +73,9 @@ type PushPull = 'push' | 'pull'; export function vcsSegmentEventProperties( type: 'git', action: PushPull | `force_${PushPull}` | - 'create_branch' | 'merge_branch' | 'delete_branch' | 'checkout_branch' | - 'commit' | 'stage_all' | 'stage' | 'unstage_all' | 'unstage' | 'rollback' | 'rollback_all' | - 'update' | 'setup' | 'clone', + 'create_branch' | 'merge_branch' | 'delete_branch' | 'checkout_branch' | + 'commit' | 'stage_all' | 'stage' | 'unstage_all' | 'unstage' | 'rollback' | 'rollback_all' | + 'update' | 'setup' | 'clone', error?: string ) { return { @@ -138,7 +101,6 @@ interface QueuedSegmentEvent { let queuedEvents: QueuedSegmentEvent[] = []; async function flushQueuedEvents() { - console.log(`[segment] Flushing ${queuedEvents.length} queued events`, queuedEvents); const events = [...queuedEvents]; // Clear queue before we even start sending to prevent races @@ -173,52 +135,21 @@ export async function trackSegmentEvent( properties, timestamp: new Date(), }; - console.log('[segment] Queued event', queuedEvent); queuedEvents.push(queuedEvent); } return; } + sendSegment('track', { + event, + properties, + ...(timestamp ? { timestamp } : {}), + }); +} - try { - if (!segmentClient) { - segmentClient = new Analytics(getSegmentWriteKey(), { - // @ts-expect-error -- TSCONVERSION - axiosConfig: { - // This is needed to ensure that we use the NodeJS adapter in the render process - ...(global?.require && { - adapter: global.require('axios/lib/adapters/http'), - }), - }, - }); - } - - const anonymousId = await getDeviceId(); - // This may return an empty string or undefined when a user is not logged in - const userId = getAccountId(); - segmentClient.track({ - anonymousId, - userId, - event, - properties, - ...(timestamp ? { timestamp } : {}), - context: { - app: { - name: getAppName(), - version: getAppVersion(), - }, - os: { - name: _getOsName(), - version: process.getSystemVersion(), - }, - }, - }, error => { - if (error) { - console.warn('[analytics] Error sending segment event', error); - } - }); - } catch (error: unknown) { - console.warn('[analytics] Unexpected error while sending segment event', error); - } +export async function trackPageView(name: string) { + const settings = await models.settings.getOrCreate(); + if (!settings.enableAnalytics) return; + sendSegment('page', { name }); } // ~~~~~~~~~~~~~~~~~ // @@ -226,156 +157,7 @@ export async function trackSegmentEvent( // ~~~~~~~~~~~~~~~~~ // function _getOsName() { const platform = getAppPlatform(); - - switch (platform) { - case 'darwin': - return 'mac'; - - case 'win32': - return 'windows'; - - default: - return platform; - } -} - -// Exported for testing -export async function _trackEvent({ - interactive, - category, - action, - label, - value, -}: { - interactive: boolean; - category: string; - action: string; - label?: string | null; - value?: string | null; -}) { - const prefix = interactive ? '[ga] Event' : '[ga] Non-interactive'; - console.log(prefix, [category, action, label, value].filter(Boolean).join(', ')); - const params = [ - { - name: KEY_HIT_TYPE, - value: 'event', - }, - { - name: KEY_EVENT_CATEGORY, - value: category, - }, - { - name: KEY_EVENT_ACTION, - value: action, - }, - - ...(!interactive ? [{ - name: KEY_NON_INTERACTION, - value: '1', - }] : []), - - ...(label ? [{ - name: KEY_EVENT_LABEL, - value: label, - }] : []), - - ...(value ? [{ - name: KEY_EVENT_VALUE, - value: value, - }] : []), - ]; - - await _sendToGoogle({ params }); -} - -export async function _trackPageView(location: string) { - _currentLocationPath = location; - console.log('[ga] Page', _currentLocationPath); - const params = [ - { - name: KEY_HIT_TYPE, - value: 'pageview', - }, - ]; - await _sendToGoogle({ params }); -} - -async function _getDefaultParams(): Promise { - const deviceId = await getDeviceId(); - // Prepping user agent string prior to sending to GA due to Electron base UA not being GA friendly. - const ua = String(window?.navigator?.userAgent) - .replace(new RegExp(`${getAppId()}\\/\\d+\\.\\d+\\.\\d+ `), '') - .replace(/Electron\/\d+\.\d+\.\d+ /, ''); - - const viewport = getViewportSize(); - - const params = [ - { - name: KEY_VERSION, - value: '1', - }, - { - name: KEY_TRACKING_ID, - value: getGoogleAnalyticsId(), - }, - { - name: KEY_CLIENT_ID, - value: deviceId, - }, - { - name: KEY_USER_AGENT, - value: ua, - }, - { - name: KEY_LOCATION, - value: getGoogleAnalyticsLocation() + _currentLocationPath, - }, - { - name: KEY_SCREEN_RESOLUTION, - value: getScreenResolution(), - }, - { - name: KEY_USER_LANGUAGE, - value: getUserLanguage(), - }, - { - name: KEY_TITLE, - value: `${getAppId()}:${getAppVersion()}`, - }, - { - name: KEY_CUSTOM_DIMENSION_PREFIX + DIMENSION_PLATFORM, - value: getAppPlatform(), - }, - { - name: KEY_CUSTOM_DIMENSION_PREFIX + DIMENSION_VERSION, - value: getAppVersion(), - }, - { - name: KEY_ANONYMIZE_IP, - value: '1', - }, - { - name: KEY_APPLICATION_NAME, - value: getAppName(), - }, - { - name: KEY_APPLICATION_ID, - value: getAppId(), - }, - { - name: KEY_APPLICATION_VERSION, - value: getAppVersion(), - }, - ...(viewport ? [{ - name: KEY_VIEWPORT_SIZE, - value: viewport, - }] : []), - ...(global.document ? [{ - name: KEY_DOCUMENT_ENCODING, - value: global.document.inputEncoding, - }] : []), - ]; - return params; + return { darwin: 'mac', win32: 'windows' }[platform] || platform; } // Monitor database changes to see if analytics gets enabled. @@ -383,72 +165,9 @@ async function _getDefaultParams(): Promise { db.onChange(async changes => { for (const change of changes) { const [event, doc] = change; - - if (isSettings(doc) && event === 'update') { - if (doc.enableAnalytics) { - await flushQueuedEvents(); - } + const isUpdatingSettings = isSettings(doc) && event === 'update'; + if (isUpdatingSettings && doc.enableAnalytics) { + await flushQueuedEvents(); } } }); - -async function _sendToGoogle({ params }: { params: RequestParameter[] }) { - const settings = await models.settings.getOrCreate(); - - if (!settings.enableAnalytics) { - return; - } - - const baseParams = await _getDefaultParams(); - const allParams = [...baseParams, ...params]; - const qs = buildQueryStringFromParams(allParams); - const baseUrl = isDevelopment() - ? 'https://www.google-analytics.com/debug/collect' - : 'https://www.google-analytics.com/collect'; - const url = joinUrlAndQueryString(baseUrl, qs); - const net = (electron.remote || electron).net; - const request = net.request(url); - request.once('error', err => { - console.warn('[ga] Network error', err); - }); - request.once('response', response => { - const { statusCode } = response; - - if (statusCode < 200 && statusCode >= 300) { - console.warn('[ga] Bad status code ' + statusCode); - } - - const chunks: Buffer[] = []; - const [contentType] = response.headers['content-type'] || []; - - if (contentType !== 'application/json') { - // Production GA API returns a Gif to use for tracking - return; - } - - response.on('end', () => { - const jsonStr = Buffer.concat(chunks).toString('utf8'); - - try { - const data = JSON.parse(jsonStr); - const { hitParsingResult } = data; - - if (hitParsingResult.valid) { - return; - } - - for (const result of hitParsingResult || []) { - for (const msg of result.parserMessage || []) { - console.warn(`[ga] Error ${msg.description}`); - } - } - } catch (err) { - console.warn('[ga] Failed to parse response', err); - } - }); - response.on('data', chunk => { - chunks.push(chunk); - }); - }); - request.end(); -} diff --git a/packages/insomnia-app/app/common/export.ts b/packages/insomnia-app/app/common/export.ts index 84297d3c9..c69ef1dc7 100644 --- a/packages/insomnia-app/app/common/export.ts +++ b/packages/insomnia-app/app/common/export.ts @@ -16,7 +16,7 @@ import { isUnitTest } from '../models/unit-test'; import { isUnitTestSuite } from '../models/unit-test-suite'; import { isWorkspace, Workspace } from '../models/workspace'; import { resetKeys } from '../sync/ignore-keys'; -import { trackEvent } from './analytics'; +import { SegmentEvent, trackSegmentEvent } from './analytics'; import { EXPORT_TYPE_API_SPEC, EXPORT_TYPE_COOKIE_JAR, @@ -113,7 +113,7 @@ export async function exportRequestsHAR( } const data = await har.exportHar(harRequests); - trackEvent('Data', 'Export', 'HAR'); + trackSegmentEvent(SegmentEvent.dataExport); return JSON.stringify(data, null, '\t'); } @@ -247,7 +247,7 @@ export async function exportRequestsData( delete d.type; return d; }); - trackEvent('Data', 'Export', `Insomnia ${format}`); + trackSegmentEvent(SegmentEvent.dataExport); if (format.toLowerCase() === 'yaml') { return YAML.stringify(data); diff --git a/packages/insomnia-app/app/common/import.ts b/packages/insomnia-app/app/common/import.ts index 964e6ca40..75a69e592 100644 --- a/packages/insomnia-app/app/common/import.ts +++ b/packages/insomnia-app/app/common/import.ts @@ -9,7 +9,7 @@ import { isWorkspace, Workspace } from '../models/workspace'; import { AlertModal } from '../ui/components/modals/alert-modal'; import { showError, showModal } from '../ui/components/modals/index'; import { ImportToWorkspacePrompt, SetWorkspaceScopePrompt } from '../ui/redux/modules/helpers'; -import { trackEvent } from './analytics'; +import { SegmentEvent, trackSegmentEvent } from './analytics'; import { BASE_ENVIRONMENT_ID_KEY, CONTENT_TYPE_GRAPHQL, @@ -321,7 +321,7 @@ export async function importRaw( } await db.flushChanges(); - trackEvent('Data', 'Import', resultsType.id); + trackSegmentEvent(SegmentEvent.dataImport); const importRequest: ImportResult = { source: resultsType && typeof resultsType.id === 'string' ? resultsType.id : 'unknown', summary: importedDocs, diff --git a/packages/insomnia-app/app/common/migrate-from-designer.ts b/packages/insomnia-app/app/common/migrate-from-designer.ts index fb20eef05..c80cf931a 100644 --- a/packages/insomnia-app/app/common/migrate-from-designer.ts +++ b/packages/insomnia-app/app/common/migrate-from-designer.ts @@ -9,7 +9,6 @@ import * as models from '../models'; import { getModelName } from '../models'; import type { Settings } from '../models/settings'; import { forceWorkspaceScopeToDesign } from '../sync/git/force-workspace-scope-to-design'; -import { trackEvent } from './analytics'; import { database as db } from './database'; async function loadDesignerDb( @@ -158,7 +157,6 @@ export default async function migrateFromDesigner({ const modelTypesToMerge = []; if (useDesignerSettings) { - trackEvent('Data', 'Migration', 'Settings'); // @ts-expect-error -- TSCONVERSION modelTypesToMerge.push(models.settings.type); console.log('[db-merge] keeping settings from Insomnia Designer'); @@ -167,7 +165,6 @@ export default async function migrateFromDesigner({ } if (copyWorkspaces) { - trackEvent('Data', 'Migration', 'Workspaces'); // @ts-expect-error -- TSCONVERSION modelTypesToMerge.push(...workspaceModels); } @@ -222,17 +219,14 @@ export default async function migrateFromDesigner({ if (copyPlugins) { console.log('[db-merge] migrating plugins from designer to core'); - trackEvent('Data', 'Migration', 'Plugins'); await migratePlugins(designerDataDir, coreDataDir); } console.log('[db-merge] done!'); - trackEvent('Data', 'Migration', 'Success'); return {}; } catch (error) { console.log('[db-merge] an error occurred while migrating'); console.error(error); - trackEvent('Data', 'Migration', 'Failure'); await restoreCoreBackup(backupDir, coreDataDir); return { error, diff --git a/packages/insomnia-app/app/sync/git/git-vcs.ts b/packages/insomnia-app/app/sync/git/git-vcs.ts index a59233b51..866c8bf39 100644 --- a/packages/insomnia-app/app/sync/git/git-vcs.ts +++ b/packages/insomnia-app/app/sync/git/git-vcs.ts @@ -1,7 +1,6 @@ import * as git from 'isomorphic-git'; import path from 'path'; -import { trackEvent } from '../../common/analytics'; import { httpClient } from './http-client'; import { convertToOsSep, convertToPosixSep } from './path-sep'; import { gitCallbacks } from './utils'; @@ -223,7 +222,6 @@ export class GitVCS { async commit(message: string) { console.log(`[git] Commit "${message}"`); - trackEvent('Git', 'Commit'); return git.commit({ ...this._baseOpts, message }); } @@ -264,7 +262,6 @@ export class GitVCS { async push(gitCredentials?: GitCredentials | null, force = false) { console.log(`[git] Push remote=origin force=${force ? 'true' : 'false'}`); - trackEvent('Git', 'Push'); // eslint-disable-next-line no-unreachable const response: git.PushResult = await git.push({ ...this._baseOpts, @@ -286,7 +283,6 @@ export class GitVCS { async pull(gitCredentials?: GitCredentials | null) { console.log('[git] Pull remote=origin', await this.getBranch()); - trackEvent('Git', 'Pull'); return git.pull({ ...this._baseOpts, ...gitCallbacks(gitCredentials), @@ -298,7 +294,6 @@ export class GitVCS { async merge(theirBranch: string) { const ours = await this.getBranch(); console.log(`[git] Merge ${ours} <-- ${theirBranch}`); - trackEvent('Git', 'Merge'); return git.merge({ ...this._baseOpts, ours, @@ -336,13 +331,11 @@ export class GitVCS { } async branch(branch: string, checkout = false) { - trackEvent('Git', 'Create Branch'); // @ts-expect-error -- TSCONVERSION remote doesn't exist as an option await git.branch({ ...this._baseOpts, ref: branch, checkout, remote: 'origin' }); } async deleteBranch(branch: string) { - trackEvent('Git', 'Delete Branch'); await git.deleteBranch({ ...this._baseOpts, ref: branch }); } @@ -353,7 +346,6 @@ export class GitVCS { const branches = await this.listBranches(); if (branches.includes(branch)) { - trackEvent('Git', 'Checkout Branch'); await git.checkout({ ...this._baseOpts, ref: branch, remote: 'origin' }); } else { await this.branch(branch, true); diff --git a/packages/insomnia-app/app/ui/components/dropdowns/content-type-dropdown.tsx b/packages/insomnia-app/app/ui/components/dropdowns/content-type-dropdown.tsx index 5b8036fb6..142e98e6e 100644 --- a/packages/insomnia-app/app/ui/components/dropdowns/content-type-dropdown.tsx +++ b/packages/insomnia-app/app/ui/components/dropdowns/content-type-dropdown.tsx @@ -1,7 +1,7 @@ import { autoBindMethodsForReact } from 'class-autobind-decorator'; import React, { PureComponent } from 'react'; -import { trackEvent } from '../../../common/analytics'; +import { SegmentEvent, trackSegmentEvent } from '../../../common/analytics'; import { AUTOBIND_CFG, CONTENT_TYPE_EDN, @@ -72,7 +72,7 @@ export class ContentTypeDropdown extends PureComponent { } this.props.onChange(mimeType); - trackEvent('Request', 'Change MimeType', mimeType); + trackSegmentEvent(SegmentEvent.requestBodyTypeSelect, { type:mimeType }); } _renderDropdownItem(mimeType: string | null, forcedName = '') { diff --git a/packages/insomnia-app/app/ui/components/dropdowns/git-sync-dropdown.tsx b/packages/insomnia-app/app/ui/components/dropdowns/git-sync-dropdown.tsx index 17c46eda7..6fd5a90cd 100644 --- a/packages/insomnia-app/app/ui/components/dropdowns/git-sync-dropdown.tsx +++ b/packages/insomnia-app/app/ui/components/dropdowns/git-sync-dropdown.tsx @@ -4,7 +4,7 @@ import React, { Fragment, PureComponent, ReactNode } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { SegmentEvent, trackEvent, trackSegmentEvent, vcsSegmentEventProperties } from '../../../common/analytics'; +import { SegmentEvent, trackSegmentEvent, vcsSegmentEventProperties } from '../../../common/analytics'; import { AUTOBIND_CFG } from '../../../common/constants'; import { database as db } from '../../../common/database'; import { docsGitSync } from '../../../common/documentation'; @@ -111,7 +111,7 @@ class GitSyncDropdown extends PureComponent { } async _handleOpen() { - trackEvent('Git Dropdown', 'Open'); + trackSegmentEvent(SegmentEvent.vcsSyncStart, vcsSegmentEventProperties('git', 'setup')); await this._refreshState(); } diff --git a/packages/insomnia-app/app/ui/components/modals/index.ts b/packages/insomnia-app/app/ui/components/modals/index.ts index dde2012c7..8149e52d2 100644 --- a/packages/insomnia-app/app/ui/components/modals/index.ts +++ b/packages/insomnia-app/app/ui/components/modals/index.ts @@ -1,4 +1,4 @@ -import { trackEvent } from '../../../common/analytics'; +import { trackPageView } from '../../../common/analytics'; import { AlertModal, AlertModalOptions } from './alert-modal'; import { ErrorModal, ErrorModalOptions } from './error-modal'; import { PromptModal, PromptModalOptions } from './prompt-modal'; @@ -15,7 +15,7 @@ export function registerModal(instance) { } export function showModal(modalCls, ...args) { - trackEvent('Modals', 'Show', modalCls.name); + trackPageView(modalCls.name); return _getModal(modalCls).show(...args); } diff --git a/packages/insomnia-app/app/ui/components/modals/request-create-modal.tsx b/packages/insomnia-app/app/ui/components/modals/request-create-modal.tsx index 55b210132..9d9d253ac 100644 --- a/packages/insomnia-app/app/ui/components/modals/request-create-modal.tsx +++ b/packages/insomnia-app/app/ui/components/modals/request-create-modal.tsx @@ -1,7 +1,7 @@ import { autoBindMethodsForReact } from 'class-autobind-decorator'; import React, { PureComponent } from 'react'; -import { trackEvent } from '../../../common/analytics'; +import { SegmentEvent, trackSegmentEvent } from '../../../common/analytics'; import { AUTOBIND_CFG, getContentTypeName, @@ -97,7 +97,7 @@ export class RequestCreateModal extends PureComponent<{}, State> { } this.hide(); - trackEvent('Request', 'Create'); + trackSegmentEvent(SegmentEvent.requestCreate); } _handleChangeSelectedContentType(selectedContentType: string | null) { diff --git a/packages/insomnia-app/app/ui/components/settings/general.tsx b/packages/insomnia-app/app/ui/components/settings/general.tsx index a818fd805..fd502d168 100644 --- a/packages/insomnia-app/app/ui/components/settings/general.tsx +++ b/packages/insomnia-app/app/ui/components/settings/general.tsx @@ -3,7 +3,6 @@ import { Tooltip } from 'insomnia-components'; import React, { FC, Fragment, useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { trackEvent } from '../../../common/analytics'; import { ACTIVITY_MIGRATION, EditorKeyMap, @@ -102,7 +101,6 @@ export const General: FC = ({ hideModal }) => { const settings = useSelector(selectSettings); const handleStartMigration = useCallback(() => { - trackEvent('Data', 'Migration', 'Manual'); dispatch(setActiveActivity(ACTIVITY_MIGRATION)); hideModal(); }, [hideModal, dispatch]); diff --git a/packages/insomnia-app/app/ui/components/spec-editor/spec-editor-sidebar.tsx b/packages/insomnia-app/app/ui/components/spec-editor/spec-editor-sidebar.tsx index 8e1a44b58..4dee9df2a 100644 --- a/packages/insomnia-app/app/ui/components/spec-editor/spec-editor-sidebar.tsx +++ b/packages/insomnia-app/app/ui/components/spec-editor/spec-editor-sidebar.tsx @@ -5,7 +5,6 @@ import styled from 'styled-components'; import YAML from 'yaml'; import YAMLSourceMap from 'yaml-source-map'; -import { trackEvent } from '../../../common/analytics'; import { AUTOBIND_CFG } from '../../../common/constants'; import type { ApiSpec } from '../../../models/api-spec'; @@ -41,7 +40,6 @@ export class SpecEditorSidebar extends Component { col: number; }; }) { - trackEvent('Spec Sidebar', 'Navigate'); const { handleSetSelection } = this.props; // NOTE: We're subtracting 1 from everything because YAML CST uses // 1-based indexing and we use 0-based. diff --git a/packages/insomnia-app/app/ui/components/wrapper-migration.tsx b/packages/insomnia-app/app/ui/components/wrapper-migration.tsx index 91c3da48c..b038cb1ba 100644 --- a/packages/insomnia-app/app/ui/components/wrapper-migration.tsx +++ b/packages/insomnia-app/app/ui/components/wrapper-migration.tsx @@ -3,7 +3,6 @@ import React, { FunctionComponent, useCallback, useMemo, useState } from 'react' import { useDispatch } from 'react-redux'; import { useMount } from 'react-use'; -import { trackEvent } from '../../common/analytics'; import { ACTIVITY_HOME } from '../../common/constants'; import { getDataDirectory, getDesignerDataDir, restartApp } from '../../common/electron-helpers'; import type { MigrationOptions } from '../../common/migrate-from-designer'; @@ -276,7 +275,6 @@ const MigrationBody = () => { const reduxDispatch = useDispatch(); const cancel = useCallback(() => { - trackEvent('Data', 'Migration', 'Skip'); reduxDispatch(setActiveActivity(ACTIVITY_HOME)); }, [reduxDispatch]); diff --git a/packages/insomnia-app/app/ui/components/wrapper.tsx b/packages/insomnia-app/app/ui/components/wrapper.tsx index ba9b79b38..3b7395b75 100644 --- a/packages/insomnia-app/app/ui/components/wrapper.tsx +++ b/packages/insomnia-app/app/ui/components/wrapper.tsx @@ -2,7 +2,6 @@ import { autoBindMethodsForReact } from 'class-autobind-decorator'; import * as importers from 'insomnia-importers'; import React, { Fragment, PureComponent, Ref } from 'react'; -import { trackPageView } from '../../common/analytics'; import type { GlobalActivity } from '../../common/constants'; import { ACTIVITY_DEBUG, @@ -439,21 +438,6 @@ export class Wrapper extends PureComponent { }); } - componentDidMount() { - const { activity } = this.props; - trackPageView(`/${activity || ''}`); - } - - componentDidUpdate(prevProps: WrapperProps) { - // We're using activities as page views so here we monitor - // for a change in activity and send it as a pageview. - const { activity } = this.props; - - if (prevProps.activity !== activity) { - trackPageView(`/${activity || ''}`); - } - } - render() { const { activeCookieJar, diff --git a/packages/insomnia-app/app/ui/index.tsx b/packages/insomnia-app/app/ui/index.tsx index dc28cf1d0..d6a366df5 100644 --- a/packages/insomnia-app/app/ui/index.tsx +++ b/packages/insomnia-app/app/ui/index.tsx @@ -6,7 +6,7 @@ import { hot } from 'react-hot-loader'; import { Provider } from 'react-redux'; import * as styledComponents from 'styled-components'; -import { trackEvent } from '../common/analytics'; +import { SegmentEvent, trackSegmentEvent } from '../common/analytics'; import { getAppLongName, isDevelopment } from '../common/constants'; import { database as db } from '../common/database'; import { initializeLogging } from '../common/log'; @@ -69,11 +69,11 @@ window['styled-components'] = styledComponents; if (window && !isDevelopment()) { window.addEventListener('error', e => { console.error('Uncaught Error', e.error || e); - trackEvent('Error', 'Uncaught Error'); + trackSegmentEvent(SegmentEvent.criticalError, { detail: e?.message }); }); window.addEventListener('unhandledrejection', e => { console.error('Unhandled Promise', e.reason); - trackEvent('Error', 'Uncaught Promise'); + trackSegmentEvent(SegmentEvent.criticalError, { detail: e?.reason }); }); } diff --git a/packages/insomnia-app/app/ui/redux/modules/__tests__/git.test.tsx b/packages/insomnia-app/app/ui/redux/modules/__tests__/git.test.tsx index d860e331c..b0a43ef9a 100644 --- a/packages/insomnia-app/app/ui/redux/modules/__tests__/git.test.tsx +++ b/packages/insomnia-app/app/ui/redux/modules/__tests__/git.test.tsx @@ -8,7 +8,7 @@ import { mocked } from 'ts-jest/utils'; import { globalBeforeEach } from '../../../../__jest__/before-each'; import { reduxStateForTest } from '../../../../__jest__/redux-state-for-test'; -import { SegmentEvent, trackEvent, trackSegmentEvent } from '../../../../common/analytics'; +import { SegmentEvent, trackSegmentEvent } from '../../../../common/analytics'; import { ACTIVITY_SPEC } from '../../../../common/constants'; import * as models from '../../../../models'; import { gitRepositorySchema } from '../../../../models/__schemas__/model-schemas'; @@ -84,7 +84,6 @@ describe('git', () => { const shouldPromptToCreateWorkspace = async (memClient: PromiseFsClient) => { const { repoSettings } = await dispatchCloneAndSubmitSettings(memClient); - expect(trackEvent).toHaveBeenCalledWith('Git', 'Clone'); // show alert asking to create a document const alertArgs = getAndClearShowAlertMockArgs(); expect(alertArgs.title).toBe('No document found'); @@ -111,7 +110,6 @@ describe('git', () => { expect(meta?.gitRepositoryId).toBe(repoSettings._id); // Ensure tracking events expect(trackSegmentEvent).toHaveBeenCalledWith(SegmentEvent.documentCreate); - expect(trackEvent).toHaveBeenCalledWith('Workspace', 'Create'); // Ensure activity is activated expect(store.getActions()).toEqual([ { diff --git a/packages/insomnia-app/app/ui/redux/modules/__tests__/global.test.ts b/packages/insomnia-app/app/ui/redux/modules/__tests__/global.test.ts index 548aab4b4..88e081795 100644 --- a/packages/insomnia-app/app/ui/redux/modules/__tests__/global.test.ts +++ b/packages/insomnia-app/app/ui/redux/modules/__tests__/global.test.ts @@ -3,7 +3,6 @@ import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import { globalBeforeEach } from '../../../../__jest__/before-each'; -import { trackEvent } from '../../../../common/analytics'; import { ACTIVITY_DEBUG, ACTIVITY_HOME, @@ -68,7 +67,6 @@ describe('global', () => { activity, }; expect(setActiveActivity(activity)).toStrictEqual(expectedEvent); - expect(trackEvent).toHaveBeenCalledWith('Activity', 'Change', activity); expect(global.localStorage.getItem(`${LOCALSTORAGE_PREFIX}::activity`)).toBe( JSON.stringify(activity), ); diff --git a/packages/insomnia-app/app/ui/redux/modules/__tests__/project.test.ts b/packages/insomnia-app/app/ui/redux/modules/__tests__/project.test.ts index b68a20016..726dcdfb7 100644 --- a/packages/insomnia-app/app/ui/redux/modules/__tests__/project.test.ts +++ b/packages/insomnia-app/app/ui/redux/modules/__tests__/project.test.ts @@ -3,7 +3,7 @@ import thunk from 'redux-thunk'; import { globalBeforeEach } from '../../../../__jest__/before-each'; import { reduxStateForTest } from '../../../../__jest__/redux-state-for-test'; -import { SegmentEvent, trackEvent, trackSegmentEvent } from '../../../../common/analytics'; +import { SegmentEvent, trackSegmentEvent } from '../../../../common/analytics'; import { ACTIVITY_HOME } from '../../../../common/constants'; import * as models from '../../../../models'; import { DEFAULT_PROJECT_ID } from '../../../../models/project'; @@ -49,7 +49,6 @@ describe('project', () => { const project = projects[1]; expect(project.name).toBe(projectName); expect(trackSegmentEvent).toHaveBeenCalledWith(SegmentEvent.projectLocalCreate); - expect(trackEvent).toHaveBeenCalledWith('Project', 'Create'); expect(store.getActions()).toEqual([ { type: SET_ACTIVE_PROJECT, @@ -93,7 +92,6 @@ describe('project', () => { const project = projects[1]; expect(project).toStrictEqual(projectTwo); expect(trackSegmentEvent).toHaveBeenCalledWith(SegmentEvent.projectLocalDelete); - expect(trackEvent).toHaveBeenCalledWith('Project', 'Delete'); expect(store.getActions()).toEqual([ { type: SET_ACTIVE_PROJECT, diff --git a/packages/insomnia-app/app/ui/redux/modules/__tests__/workspace.test.ts b/packages/insomnia-app/app/ui/redux/modules/__tests__/workspace.test.ts index 3d583e8e5..780c0dd6f 100644 --- a/packages/insomnia-app/app/ui/redux/modules/__tests__/workspace.test.ts +++ b/packages/insomnia-app/app/ui/redux/modules/__tests__/workspace.test.ts @@ -3,7 +3,7 @@ import thunk from 'redux-thunk'; import { globalBeforeEach } from '../../../../__jest__/before-each'; import { reduxStateForTest } from '../../../../__jest__/redux-state-for-test'; -import { SegmentEvent, trackEvent, trackSegmentEvent } from '../../../../common/analytics'; +import { SegmentEvent, trackSegmentEvent } from '../../../../common/analytics'; import { ACTIVITY_DEBUG, ACTIVITY_SPEC, ACTIVITY_UNIT_TEST } from '../../../../common/constants'; import { database } from '../../../../common/database'; import * as models from '../../../../models'; @@ -62,7 +62,6 @@ describe('workspace', () => { const workspaceId = await expectedModelsCreated(workspaceName, WorkspaceScopeKeys.design, projectId); expect(trackSegmentEvent).toHaveBeenCalledWith(SegmentEvent.documentCreate); - expect(trackEvent).toHaveBeenCalledWith('Workspace', 'Create'); expect(store.getActions()).toEqual([ { type: SET_ACTIVE_PROJECT, @@ -96,7 +95,6 @@ describe('workspace', () => { const workspaceId = await expectedModelsCreated(workspaceName, WorkspaceScopeKeys.collection, projectId); expect(trackSegmentEvent).toHaveBeenCalledWith(SegmentEvent.collectionCreate); - expect(trackEvent).toHaveBeenCalledWith('Workspace', 'Create'); expect(store.getActions()).toEqual([ { type: SET_ACTIVE_PROJECT, diff --git a/packages/insomnia-app/app/ui/redux/modules/git.tsx b/packages/insomnia-app/app/ui/redux/modules/git.tsx index 805294bae..f6c4439af 100644 --- a/packages/insomnia-app/app/ui/redux/modules/git.tsx +++ b/packages/insomnia-app/app/ui/redux/modules/git.tsx @@ -3,7 +3,7 @@ import path from 'path'; import React, { ReactNode } from 'react'; import YAML from 'yaml'; -import { SegmentEvent, trackEvent, trackSegmentEvent, vcsSegmentEventProperties } from '../../../common/analytics'; +import { SegmentEvent, trackSegmentEvent, vcsSegmentEventProperties } from '../../../common/analytics'; import { database as db } from '../../../common/database'; import { strings } from '../../../common/strings'; import * as models from '../../../models'; @@ -164,7 +164,6 @@ export const cloneGitRepository = ({ createFsClient }: { dispatch(loadStart()); repoSettingsPatch.needsFullClone = true; repoSettingsPatch.uri = translateSSHtoHTTP(repoSettingsPatch.uri); - trackEvent('Git', 'Clone'); let fsClient = createFsClient(); try { diff --git a/packages/insomnia-app/app/ui/redux/modules/global.tsx b/packages/insomnia-app/app/ui/redux/modules/global.tsx index b293fab4c..ae9994b76 100644 --- a/packages/insomnia-app/app/ui/redux/modules/global.tsx +++ b/packages/insomnia-app/app/ui/redux/modules/global.tsx @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { combineReducers, Dispatch } from 'redux'; import { unreachableCase } from 'ts-assert-unreachable'; -import { trackEvent } from '../../../common/analytics'; +import { trackPageView } from '../../../common/analytics'; import type { DashboardSortOrder, GlobalActivity } from '../../../common/constants'; import { ACTIVITY_DEBUG, @@ -319,7 +319,7 @@ export const loadRequestStop = (requestId: string) => ({ export const setActiveActivity = (activity: GlobalActivity) => { activity = _normalizeActivity(activity); window.localStorage.setItem(`${LOCALSTORAGE_PREFIX}::activity`, JSON.stringify(activity)); - trackEvent('Activity', 'Change', activity); + trackPageView(activity); return { type: SET_ACTIVE_ACTIVITY, activity, @@ -690,7 +690,6 @@ export const initActiveActivity = () => (dispatch, getState) => { } else { // Always check if user has been prompted to migrate or onboard if (!settings.hasPromptedToMigrateFromDesigner && fs.existsSync(getDesignerDataDir())) { - trackEvent('Data', 'Migration', 'Auto'); overrideActivity = ACTIVITY_MIGRATION; } } diff --git a/packages/insomnia-app/app/ui/redux/modules/project.ts b/packages/insomnia-app/app/ui/redux/modules/project.ts index afe7df2ad..07f736069 100644 --- a/packages/insomnia-app/app/ui/redux/modules/project.ts +++ b/packages/insomnia-app/app/ui/redux/modules/project.ts @@ -1,4 +1,4 @@ -import { SegmentEvent, trackEvent, trackSegmentEvent } from '../../../common/analytics'; +import { SegmentEvent, trackSegmentEvent } from '../../../common/analytics'; import { ACTIVITY_HOME } from '../../../common/constants'; import { strings } from '../../../common/strings'; import * as models from '../../../models'; @@ -18,7 +18,6 @@ export const createProject = () => dispatch => { selectText: true, onComplete: async name => { const project = await models.project.create({ name }); - trackEvent('Project', 'Create'); dispatch(setActiveProject(project._id)); dispatch(setActiveActivity(ACTIVITY_HOME)); trackSegmentEvent(SegmentEvent.projectLocalCreate); @@ -39,7 +38,6 @@ export const removeProject = (project: Project) => dispatch => { onConfirm: async () => { await models.stats.incrementDeletedRequestsForDescendents(project); await models.project.remove(project); - trackEvent('Project', 'Delete'); // Show default project dispatch(setActiveProject(DEFAULT_PROJECT_ID)); // Show home in case not already on home diff --git a/packages/insomnia-app/app/ui/redux/modules/workspace.ts b/packages/insomnia-app/app/ui/redux/modules/workspace.ts index 7608f2ece..b7ae9dd63 100644 --- a/packages/insomnia-app/app/ui/redux/modules/workspace.ts +++ b/packages/insomnia-app/app/ui/redux/modules/workspace.ts @@ -1,7 +1,7 @@ import { Dispatch } from 'redux'; import { RequireExactlyOne } from 'type-fest'; -import { SegmentEvent, trackEvent, trackSegmentEvent } from '../../../common/analytics'; +import { SegmentEvent, trackSegmentEvent } from '../../../common/analytics'; import { ACTIVITY_DEBUG, ACTIVITY_SPEC, GlobalActivity, isCollectionActivity, isDesignActivity } from '../../../common/constants'; import { database } from '../../../common/database'; import * as models from '../../../models'; @@ -31,7 +31,8 @@ const actuallyCreate = (patch: Partial, onCreate?: OnWorkspaceCreateC await onCreate(workspace); } - trackEvent('Workspace', 'Create'); + trackSegmentEvent(SegmentEvent.collectionCreate); + await dispatch(activateWorkspace({ workspace })); }; }; diff --git a/packages/insomnia-inso/package-lock.json b/packages/insomnia-inso/package-lock.json index 230dfdf9c..2479a7df5 100644 --- a/packages/insomnia-inso/package-lock.json +++ b/packages/insomnia-inso/package-lock.json @@ -2382,6 +2382,14 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, "babel-jest": { "version": "26.6.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", @@ -4299,6 +4307,11 @@ "readable-stream": "^2.3.6" } }, + "follow-redirects": { + "version": "1.14.7", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", + "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==" + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", diff --git a/packages/insomnia-inso/package.json b/packages/insomnia-inso/package.json index c0a805ca4..a4b42a3ed 100644 --- a/packages/insomnia-inso/package.json +++ b/packages/insomnia-inso/package.json @@ -69,6 +69,7 @@ }, "dependencies": { "@stoplight/spectral": "^5.9.0", + "axios": "^0.21.2", "commander": "^5.1.0", "consola": "^2.15.0", "cosmiconfig": "^6.0.0", diff --git a/plugins/insomnia-plugin-kong-portal/index.js b/plugins/insomnia-plugin-kong-portal/index.js index 10c49bff7..356b899ca 100644 --- a/plugins/insomnia-plugin-kong-portal/index.js +++ b/plugins/insomnia-plugin-kong-portal/index.js @@ -13,7 +13,7 @@ export const documentActions = [ spec={spec} store={context.store} axios={context.__private.axios} - trackEvent={context.__private.analytics.trackEvent} + trackSegmentEvent={context.__private.analytics.trackSegmentEvent} />, root, ); diff --git a/plugins/insomnia-plugin-kong-portal/src/deploy-to-portal.js b/plugins/insomnia-plugin-kong-portal/src/deploy-to-portal.js index fdd07d588..900a00b89 100644 --- a/plugins/insomnia-plugin-kong-portal/src/deploy-to-portal.js +++ b/plugins/insomnia-plugin-kong-portal/src/deploy-to-portal.js @@ -12,7 +12,7 @@ type Props = { data: Object, status: number, }>, - trackEvent: (category: string, action: string, label: ?string, value: ?string) => any, + trackSegmentEvent: (event: string, properties?: Record) => any, store: { hasItem: (key: string) => Promise, setItem: (key: string, value: string) => Promise, @@ -90,7 +90,7 @@ class DeployToPortal extends React.Component { e.preventDefault(); } - const { spec, axios, trackEvent } = this.props; + const { spec, axios, trackSegmentEvent } = this.props; const { kongSpecFileName, @@ -145,12 +145,14 @@ class DeployToPortal extends React.Component { }); if (response.statusText === 'Created' || response.statusText === 'OK') { this.setState({ kongPortalDeployView: 'success' }); - trackEvent('Portal', 'Upload', overwrite ? 'Replace' : 'Create'); + const action = overwrite ? 'replace_portal' : 'create_portal' + trackSegmentEvent('Kong Synced', { type: 'deploy', action }) } } catch (err) { if (err.response && err.response.status === 409) { this.setState({ kongPortalDeployView: 'overwrite' }); - trackEvent('Portal', 'Upload Error', overwrite ? 'Replace' : 'Create'); + const action = overwrite ? 'replace_portal' : 'create_portal' + trackSegmentEvent('Kong Synced', { type: 'deploy', action, error: err.response.status + ': ' + err.response.statusText }) } else { console.log('Failed to upload to dev portal', err.response); if (err.response && err.response.data && err.response.data.message) { @@ -164,7 +166,7 @@ class DeployToPortal extends React.Component { async _handleConnectKong(e: SyntheticEvent) { e.preventDefault(); - const { axios, trackEvent } = this.props; + const { axios, trackSegmentEvent } = this.props; const { kongPortalUserWorkspace, kongPortalApiUrl, kongPortalRbacToken } = this.state; @@ -182,7 +184,8 @@ class DeployToPortal extends React.Component { }, }); if (response.status === 200 || response.status === 201) { - trackEvent('Portal', 'Connection'); + trackSegmentEvent('Kong Connected', { type: 'token', action: 'portal_deploy' }) + // Set legacy mode for post upload formatting, suppress loader, set monitor portal URL, move to upload view const guiHost = response.data.configuration.portal_gui_host; this.setState({ @@ -195,7 +198,8 @@ class DeployToPortal extends React.Component { this._handleLoadingToggle(false); } } catch (error) { - trackEvent('Portal', 'Connection Error'); + trackSegmentEvent('Kong Connected', { type: 'token', action: 'portal_deploy', error: error.message }) + console.log('Connection error', error); this._handleLoadingToggle(false); this.setState({ connectionError: error });