mirror of
https://github.com/Kong/insomnia.git
synced 2026-08-04 11:52:33 -04:00
Merge branch 'develop' into feat/ia-nav-improvement
This commit is contained in:
26
package-lock.json
generated
26
package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"workspaces": [
|
||||
"packages/insomnia-testing",
|
||||
"packages/insomnia",
|
||||
"packages/insomnia-analytics",
|
||||
"packages/insomnia-api",
|
||||
"packages/insomnia-inso",
|
||||
"packages/insomnia-smoke-test",
|
||||
@@ -11325,9 +11326,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.12",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz",
|
||||
"integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==",
|
||||
"version": "0.8.13",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz",
|
||||
"integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -18409,6 +18410,10 @@
|
||||
"resolved": "packages/insomnia",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/insomnia-analytics": {
|
||||
"resolved": "packages/insomnia-analytics",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/insomnia-api": {
|
||||
"resolved": "packages/insomnia-api",
|
||||
"link": true
|
||||
@@ -29183,7 +29188,6 @@
|
||||
"@rjsf/utils": "6.0.0-beta.15",
|
||||
"@rjsf/validator-ajv8": "6.0.0-beta.15",
|
||||
"@seald-io/nedb": "^4.1.1",
|
||||
"@segment/analytics-node": "2.2.1",
|
||||
"@sentry/electron": "^6.5.0",
|
||||
"@stoplight/spectral-core": "^1.22.0",
|
||||
"@stoplight/spectral-formats": "^1.8.2",
|
||||
@@ -29336,6 +29340,13 @@
|
||||
"@kong/insomnia-plugin-external-vault": "0.1.4-dev.20251224090833"
|
||||
}
|
||||
},
|
||||
"packages/insomnia-analytics": {
|
||||
"version": "12.5.1-alpha.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@segment/analytics-node": "2.2.1"
|
||||
}
|
||||
},
|
||||
"packages/insomnia-api": {
|
||||
"version": "12.5.1-alpha.0",
|
||||
"license": "Apache-2.0",
|
||||
@@ -29348,7 +29359,6 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@seald-io/nedb": "^4.1.1",
|
||||
"@segment/analytics-node": "^2.2.1",
|
||||
"@stoplight/spectral-core": "^1.22.0",
|
||||
"@stoplight/spectral-formats": "^1.8.2",
|
||||
"@stoplight/spectral-ruleset-bundler": "1.7.0",
|
||||
@@ -29938,9 +29948,9 @@
|
||||
}
|
||||
},
|
||||
"packages/insomnia/node_modules/@xmldom/xmldom": {
|
||||
"version": "0.9.9",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.9.tgz",
|
||||
"integrity": "sha512-qycIHAucxy/LXAYIjmLmtQ8q9GPnMbnjG1KXhWm9o5sCr6pOYDATkMPiTNa6/v8eELyqOQ2FsEqeoFYmgv/gJg==",
|
||||
"version": "0.9.10",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz",
|
||||
"integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.6"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"workspaces": [
|
||||
"packages/insomnia-testing",
|
||||
"packages/insomnia",
|
||||
"packages/insomnia-analytics",
|
||||
"packages/insomnia-api",
|
||||
"packages/insomnia-inso",
|
||||
"packages/insomnia-smoke-test",
|
||||
|
||||
36
packages/insomnia-analytics/package.json
Normal file
36
packages/insomnia-analytics/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "insomnia-analytics",
|
||||
"license": "Apache-2.0",
|
||||
"version": "12.5.1-alpha.0",
|
||||
"author": "Kong <office@konghq.com>",
|
||||
"description": "Shared analytics client for the Insomnia desktop app and CLI",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Kong/insomnia.git",
|
||||
"directory": "packages/insomnia-analytics"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/kong/insomnia/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Kong/insomnia#readme",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
},
|
||||
"./events": {
|
||||
"import": "./src/events.ts",
|
||||
"types": "./src/events.ts"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint . --ext .ts --cache",
|
||||
"type-check": "tsc --noEmit --project tsconfig.json",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@segment/analytics-node": "2.2.1"
|
||||
}
|
||||
}
|
||||
73
packages/insomnia-analytics/src/analytics.test.ts
Normal file
73
packages/insomnia-analytics/src/analytics.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const mockTrack = vi.fn();
|
||||
const mockPage = vi.fn();
|
||||
const mockCloseAndFlush = vi.fn().mockResolvedValue(void 0);
|
||||
|
||||
vi.mock('@segment/analytics-node', () => ({
|
||||
Analytics: vi.fn(() => ({
|
||||
track: mockTrack,
|
||||
page: mockPage,
|
||||
closeAndFlush: mockCloseAndFlush,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('InsomniaAnalytics', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
mockTrack.mockReset();
|
||||
mockPage.mockReset();
|
||||
mockCloseAndFlush.mockReset().mockResolvedValue(void 0);
|
||||
});
|
||||
|
||||
it('forwards track calls with platform-tagged properties and an app+os context', async () => {
|
||||
const { InsomniaAnalytics } = await import('./analytics');
|
||||
const analytics = new InsomniaAnalytics({
|
||||
writeKey: 'test',
|
||||
app: { appName: 'inso', appVersion: '1.2.3', osVersion: '14.0', platform: 'cli' },
|
||||
});
|
||||
|
||||
analytics.track({ event: 'inso_run_test', properties: { suite: 'echo' }, anonymousId: 'anon-1' });
|
||||
|
||||
expect(mockTrack).toHaveBeenCalledTimes(1);
|
||||
const [payload] = mockTrack.mock.calls[0];
|
||||
expect(payload).toMatchObject({
|
||||
event: 'inso_run_test',
|
||||
anonymousId: 'anon-1',
|
||||
userId: '',
|
||||
properties: { suite: 'echo', platform: 'cli' },
|
||||
context: { app: { name: 'inso', version: '1.2.3' }, os: { version: '14.0' } },
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves osVersion lazily when provided as a function', async () => {
|
||||
const { InsomniaAnalytics } = await import('./analytics');
|
||||
let v = '1.0';
|
||||
const analytics = new InsomniaAnalytics({
|
||||
writeKey: 'test',
|
||||
app: { appName: 'app', appVersion: '0.1', osVersion: () => v, platform: 'app' },
|
||||
});
|
||||
|
||||
v = '2.0';
|
||||
analytics.track({ event: 'App Started', anonymousId: 'a', userId: 'u' });
|
||||
|
||||
const [payload] = mockTrack.mock.calls[0];
|
||||
expect(payload.context.os.version).toBe('2.0');
|
||||
});
|
||||
|
||||
it('routes analytics errors through the onError handler', async () => {
|
||||
const onError = vi.fn();
|
||||
const { InsomniaAnalytics } = await import('./analytics');
|
||||
const analytics = new InsomniaAnalytics({
|
||||
writeKey: 'test',
|
||||
app: { appName: 'app', appVersion: '0.1', osVersion: '1.0', platform: 'app' },
|
||||
onError,
|
||||
});
|
||||
|
||||
analytics.track({ event: 'App Started', anonymousId: 'a' });
|
||||
const [, callback] = mockTrack.mock.calls[0];
|
||||
callback(new Error('segment failure'));
|
||||
|
||||
expect(onError).toHaveBeenCalledWith(expect.any(Error));
|
||||
});
|
||||
});
|
||||
103
packages/insomnia-analytics/src/analytics.ts
Normal file
103
packages/insomnia-analytics/src/analytics.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { Analytics, type AnalyticsSettings } from '@segment/analytics-node';
|
||||
|
||||
export interface AppContext {
|
||||
appName: string;
|
||||
appVersion: string;
|
||||
osVersion: string | (() => string);
|
||||
platform: 'app' | 'cli';
|
||||
}
|
||||
|
||||
export interface TrackOptions {
|
||||
event: string;
|
||||
properties?: Record<string, unknown>;
|
||||
anonymousId: string;
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export interface PageOptions {
|
||||
name: string;
|
||||
anonymousId: string;
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export interface InsomniaAnalyticsOptions {
|
||||
writeKey: string;
|
||||
app: AppContext;
|
||||
settings?: Omit<AnalyticsSettings, 'writeKey'>;
|
||||
onError?: (error: unknown) => void;
|
||||
}
|
||||
|
||||
export function getNormalizedOsName(platform: NodeJS.Platform | string): string {
|
||||
switch (platform) {
|
||||
case 'darwin': {
|
||||
return 'mac';
|
||||
}
|
||||
case 'win32': {
|
||||
return 'windows';
|
||||
}
|
||||
default: {
|
||||
return platform;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class InsomniaAnalytics {
|
||||
private readonly client: Analytics;
|
||||
private readonly app: AppContext;
|
||||
private readonly onError: (error: unknown) => void;
|
||||
|
||||
constructor({ writeKey, app, settings, onError }: InsomniaAnalyticsOptions) {
|
||||
this.client = new Analytics({ writeKey, ...settings });
|
||||
this.app = app;
|
||||
this.onError = onError ?? (() => {});
|
||||
}
|
||||
|
||||
track({ event, properties, anonymousId, userId }: TrackOptions): void {
|
||||
this.client.track(
|
||||
{
|
||||
event,
|
||||
anonymousId,
|
||||
userId: userId ?? '',
|
||||
properties: { ...properties, platform: this.app.platform },
|
||||
context: this.buildContext(),
|
||||
},
|
||||
error => {
|
||||
if (error) {
|
||||
this.onError(error);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
page({ name, anonymousId, userId }: PageOptions): void {
|
||||
this.client.page(
|
||||
{
|
||||
name,
|
||||
anonymousId,
|
||||
userId: userId ?? '',
|
||||
context: this.buildContext(),
|
||||
},
|
||||
error => {
|
||||
if (error) {
|
||||
this.onError(error);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async closeAndFlush(timeoutMs = 5000): Promise<void> {
|
||||
try {
|
||||
await this.client.closeAndFlush({ timeout: timeoutMs });
|
||||
} catch (error) {
|
||||
this.onError(error);
|
||||
}
|
||||
}
|
||||
|
||||
private buildContext() {
|
||||
const osVersion = typeof this.app.osVersion === 'function' ? this.app.osVersion() : this.app.osVersion;
|
||||
return {
|
||||
app: { name: this.app.appName, version: this.app.appVersion },
|
||||
os: { name: getNormalizedOsName(process.platform), version: osVersion },
|
||||
};
|
||||
}
|
||||
}
|
||||
16
packages/insomnia-analytics/src/events.test.ts
Normal file
16
packages/insomnia-analytics/src/events.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { AnalyticsEvent, InsoEvent } from './events';
|
||||
|
||||
describe('events', () => {
|
||||
it('AnalyticsEvent has expected entries', () => {
|
||||
expect(AnalyticsEvent.appStarted).toBe('App Started');
|
||||
expect(AnalyticsEvent.unitTestRun).toBe('Ran Individual Unit Test');
|
||||
expect(AnalyticsEvent.installPlugin).toBe('Plugin Installed');
|
||||
});
|
||||
|
||||
it('InsoEvent prefixes with inso_', () => {
|
||||
expect(InsoEvent.runTest).toBe('inso_run_test');
|
||||
expect(InsoEvent.script).toBe('inso_script');
|
||||
});
|
||||
});
|
||||
142
packages/insomnia-analytics/src/events.ts
Normal file
142
packages/insomnia-analytics/src/events.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
export enum AnalyticsEvent {
|
||||
appStarted = 'App Started',
|
||||
analyticsDisabled = 'Analytics Disabled',
|
||||
collectionCreate = 'Collection Created',
|
||||
dataExport = 'Data Exported',
|
||||
exportCompleted = 'Export Completed',
|
||||
dataImport = 'Data Imported',
|
||||
importStarted = 'Import Started',
|
||||
importScanned = 'Import Scanned',
|
||||
importCompleted = 'Import Completed',
|
||||
importLoginRequired = 'Import Login Required',
|
||||
importResumedAfterLogin = 'Import Resumed After Login',
|
||||
importedRequestFirstSend = 'Imported Request First Send',
|
||||
documentCreate = 'Document Created',
|
||||
mockCreateModalOpened = 'Mock Server Create Modal Opened',
|
||||
mockCreate = 'Mock Created',
|
||||
mockEdit = 'Mock Server Edited',
|
||||
mockDelete = 'Mock Server Deleted',
|
||||
mockRouteCreate = 'Mock Route Created',
|
||||
mockRouteEdit = 'Mock Route Edited',
|
||||
mockRouteDelete = 'Mock Route Deleted',
|
||||
generateCollection = 'Generated Collection',
|
||||
generateCollectionFromMock = 'Generate Collection From Mock',
|
||||
environmentCreate = 'Environment Created',
|
||||
loginSuccess = 'Login Success',
|
||||
inviteTrigger = 'Invite Triggered From App',
|
||||
exportAllCollections = 'Exported All Collections',
|
||||
kongConnected = 'Kong Connected',
|
||||
kongSync = 'Kong Synced',
|
||||
requestBodyTypeSelect = 'Request Body Type Selected',
|
||||
requestCreated = 'Request Created',
|
||||
requestExecuted = 'Request Executed',
|
||||
requestEdit = 'Request Edited',
|
||||
requestDeleted = 'Request Deleted',
|
||||
requestRenamed = 'Request Renamed',
|
||||
requestUrlCopied = 'Request URL Copied',
|
||||
collectionRunExecute = 'Collection Run Executed',
|
||||
projectLocalCreate = 'Local Project Created',
|
||||
projectLocalDelete = 'Local Project Deleted',
|
||||
selectScratchpad = 'Scratchpad Selected at Login',
|
||||
syncConflictResolutionStart = 'Sync Conflict Resolution Started',
|
||||
syncConflictResolutionCompleteMine = 'Sync Conflict Resolution Completed Mine',
|
||||
syncConflictResolutionCompleteTheirs = 'Sync Conflict Resolution Completed Theirs',
|
||||
testSuiteCreate = 'Test Suite Created',
|
||||
testSuiteDelete = 'Test Suite Deleted',
|
||||
unitTestCreate = 'Unit Test Created',
|
||||
unitTestDelete = 'Unit Test Deleted',
|
||||
unitTestRun = 'Ran Individual Unit Test',
|
||||
unitTestRunAll = 'Ran All Unit Tests',
|
||||
vcsSyncStart = 'VCS Sync Started',
|
||||
vcsSyncComplete = 'VCS Sync Completed',
|
||||
vcsAction = 'VCS Action Executed',
|
||||
gitAuthenticationCompleted = 'Git Authentication Completed',
|
||||
gitAuthenticationUpdated = 'Git Authentication Updated',
|
||||
buttonClick = 'Button Clicked',
|
||||
inviteMember = 'Invite Sent',
|
||||
inviteResent = 'Invite Resent',
|
||||
inviteRevoked = 'Invite Revoked',
|
||||
projectCreated = 'Project Created',
|
||||
projectUpdated = 'Project Updated',
|
||||
exportStarted = 'Export Started',
|
||||
exportRequestsChosen = 'Export Requests Chosen',
|
||||
recommendCommitsGenerated = 'Recommend Commits Generated',
|
||||
recommendCommitsSaved = 'Recommend Commits Saved',
|
||||
recommendCommitsCancelled = 'Recommend Commits Cancelled',
|
||||
recommendCommitsClicked = 'Recommend Commits Clicked',
|
||||
mcpClientWorkspaceCreate = 'MCP Client Workspace Created',
|
||||
mcpClientAdded = 'MCP Client Added',
|
||||
mcpClientConnected = 'MCP Client Connected',
|
||||
mcpClientDisconnected = 'MCP Client Disconnected',
|
||||
mcpToolCalled = 'MCP Tool Called',
|
||||
mcpResourceRead = 'MCP Resource Read',
|
||||
mcpPromptCalled = 'MCP Prompt Called',
|
||||
inviteNotPermitted = 'Invite Not Permitted',
|
||||
responseToMockClicked = 'Response To Mock Clicked',
|
||||
gitSyncButtonClicked = 'Git Sync Button Clicked',
|
||||
preferencesViewed = 'Preferences Viewed',
|
||||
copyAsCurl = 'Copied As cURL',
|
||||
themeChanged = 'Theme Changed',
|
||||
generateCodeClicked = 'Generate Code Clicked',
|
||||
generateCodeLanguageChanged = 'Generate Code Language Changed',
|
||||
filterCreatedHomePage = 'Filter Created From Home Page',
|
||||
filterCreatedProjects = 'Filter Created Projects',
|
||||
filterCreatedRequests = 'Filter Created Requests',
|
||||
filterCreatedResponseBody = 'Filter Created Response Body',
|
||||
aiFeatureEnabled = 'AI Feature Enabled',
|
||||
aiFeatureDisabled = 'AI Feature Disabled',
|
||||
installPlugin = 'Plugin Installed',
|
||||
|
||||
homepageFiltered = 'homepage-filtered',
|
||||
quickSearchOpenedByKeyboard = 'quick-search-opened-by-keyboard',
|
||||
quickSearchOpenedByMouse = 'quick-search-opened-by-mouse',
|
||||
statusbarLeftbarToggled = 'statusbar-leftbar-toggled',
|
||||
statusbarTopbarToggled = 'statusbar-topbar-toggled',
|
||||
statusbarOrphanedProjectsClicked = 'statusbar-orphaned-projects-clicked',
|
||||
designerGenerateMockClicked = 'designer-generate-mock-clicked',
|
||||
designerPreviewToggled = 'designer-preview-toggled',
|
||||
requestEnvironmentClicked = 'request-environment-clicked',
|
||||
requestAddCookiesClicked = 'request-add-cookies-clicked',
|
||||
requestAddCertificatesClicked = 'request-add-certificates-clicked',
|
||||
requestListSortClicked = 'request-list-sort-clicked',
|
||||
requestListExpandCollapseClicked = 'request-list-expand-collapse-clicked',
|
||||
requestParamsDescriptionToggled = 'request-params-description-toggled',
|
||||
requestParamsImportFromURLClicked = 'request-params-import-from-URL-clicked',
|
||||
requestParamsBulkEditToggled = 'request-params-bulk-edit-toggled',
|
||||
responsePreviewJSONPathEntered = 'response-preview-jsonpath-entered',
|
||||
requestBodyBeautifyClicked = 'request-body-beautify-clicked',
|
||||
requestHeadersDescriptionToggled = 'request-headers-description-toggled',
|
||||
requestHeadersBulkEditToggled = 'request-headers-bulk-edit-toggled',
|
||||
requestScriptsPreScriptSnippetAdded = 'request-scripts-prescript-snippet-added',
|
||||
requestScriptsPostScriptSnippetAdded = 'request-scripts-postscript-snippet-added',
|
||||
responseHeadersCopyAllClicked = 'response-headers-copy-all-clicked',
|
||||
responseCookiesManageCookiesClicked = 'response-cookies-manage-cookies-clicked',
|
||||
requestOpenInNewTabClicked = 'request-open-in-new-tab-clicked',
|
||||
requestListMenuPinClicked = 'request-list-menu-pin-clicked',
|
||||
requestListMenuDuplicateClicked = 'request-list-menu-duplicate-clicked',
|
||||
requestListMenuRenameClicked = 'request-list-menu-rename-clicked',
|
||||
requestListMenuSettingsClicked = 'request-list-menu-settings-clicked',
|
||||
requestSendMenuGenerateCodeClicked = 'request-send-menu-generate-code-clicked',
|
||||
requestSendMenuSendAfterDelayClicked = 'request-send-menu-send-after-delay-clicked',
|
||||
requestSendMenuRepeatAfterIntervalClicked = 'request-send-menu-repeat-after-interval-clicked',
|
||||
requestSendMenuDownloadAfterSendClicked = 'request-send-menu-download-after-send-clicked',
|
||||
requestSendMenuSendAndDownloadClicked = 'request-send-menu-send-and-download-clicked',
|
||||
mcpListExpandCollapseClicked = 'mcp-list-expand-collapse-clicked',
|
||||
mcpListFiltered = 'mcp-list-filtered',
|
||||
mcpRequestParamsBeautifyClicked = 'mcp-request-params-beautify-clicked',
|
||||
mcpRequestHeadersDescriptionToggled = 'mcp-request-headers-description-toggled',
|
||||
mcpRequestRootsNotifyClicked = 'mcp-request-roots-notify-clicked',
|
||||
mcpResponseHeadersCopyAllClicked = 'mcp-response-headers-copy-all-clicked',
|
||||
kongKonnectPatValidated = 'kong-konnect-pat-validated',
|
||||
kongKonnectSyncCompleted = 'kong-konnect-sync-completed',
|
||||
emptyStateSendRequestClicked = 'empty-state-send-request-clicked',
|
||||
emptyStateCreateDocumentClicked = 'empty-state-create-document-clicked',
|
||||
}
|
||||
|
||||
export enum InsoEvent {
|
||||
runTest = 'inso_run_test',
|
||||
runCollection = 'inso_run_collection',
|
||||
lintSpec = 'inso_lint_spec',
|
||||
exportSpec = 'inso_export_spec',
|
||||
script = 'inso_script',
|
||||
}
|
||||
2
packages/insomnia-analytics/src/index.ts
Normal file
2
packages/insomnia-analytics/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './events';
|
||||
export * from './analytics';
|
||||
25
packages/insomnia-analytics/tsconfig.json
Normal file
25
packages/insomnia-analytics/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "es2020",
|
||||
"allowJs": false,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "bundler",
|
||||
"isolatedModules": true,
|
||||
"module": "ESNext",
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"rootDirs": ["."],
|
||||
"lib": ["ES2023", "DOM"],
|
||||
"types": [],
|
||||
"strict": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"verbatimModuleSyntax": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
}
|
||||
7
packages/insomnia-analytics/vitest.config.ts
Normal file
7
packages/insomnia-analytics/vitest.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'node',
|
||||
},
|
||||
});
|
||||
@@ -26,7 +26,7 @@
|
||||
"typescript": "~5.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0"
|
||||
"node": ">=24.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ai-sdk/gateway": {
|
||||
@@ -9144,9 +9144,10 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
"shellwords": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@segment/analytics-node": "^2.2.1",
|
||||
"@seald-io/nedb": "^4.1.1",
|
||||
"@stoplight/spectral-core": "^1.22.0",
|
||||
"@stoplight/spectral-formats": "^1.8.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os from 'node:os';
|
||||
|
||||
import { Analytics } from '@segment/analytics-node';
|
||||
import { getSegmentWriteKey } from 'insomnia/src/common/constants';
|
||||
import { InsoEvent, InsomniaAnalytics } from 'insomnia-analytics';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import type { Settings } from '~/insomnia-data';
|
||||
@@ -10,15 +10,18 @@ import packageJson from '../package.json';
|
||||
import neDbAdapter from './db/adapters/ne-db-adapter';
|
||||
import { getAppDataDir, getDefaultProductName } from './util';
|
||||
|
||||
export enum InsoEvent {
|
||||
runTest = 'inso_run_test',
|
||||
runCollection = 'inso_run_collection',
|
||||
lintSpec = 'inso_lint_spec',
|
||||
exportSpec = 'inso_export_spec',
|
||||
script = 'inso_script',
|
||||
}
|
||||
export { InsoEvent };
|
||||
|
||||
const analytics = new InsomniaAnalytics({
|
||||
writeKey: getSegmentWriteKey(),
|
||||
app: {
|
||||
appName: 'inso',
|
||||
appVersion: process.env.VERSION || packageJson.version,
|
||||
osVersion: () => os.release(),
|
||||
platform: 'cli',
|
||||
},
|
||||
});
|
||||
|
||||
const analyticsClient = new Analytics({ writeKey: getSegmentWriteKey() });
|
||||
let deviceId: string | null = null;
|
||||
let localSettings: Settings | null = null;
|
||||
|
||||
@@ -54,25 +57,16 @@ const getDeviceId = async (): Promise<string> => {
|
||||
return deviceId;
|
||||
};
|
||||
|
||||
const getOsName = (): string => {
|
||||
switch (process.platform) {
|
||||
case 'darwin': {
|
||||
return 'mac';
|
||||
}
|
||||
case 'win32': {
|
||||
return 'windows';
|
||||
}
|
||||
default: {
|
||||
return process.platform;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const trackInsoEvent = async (event: InsoEvent, properties?: Record<string, unknown>): Promise<void> => {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
return;
|
||||
}
|
||||
|
||||
// new for v13 - provide a way to disable analytics
|
||||
if (process.env.INSO_TELEMETRY_DISABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = await getLocalSettings();
|
||||
if (settings && !settings.enableAnalytics) {
|
||||
return;
|
||||
@@ -80,40 +74,10 @@ export const trackInsoEvent = async (event: InsoEvent, properties?: Record<strin
|
||||
|
||||
try {
|
||||
const anonymousId = await getDeviceId();
|
||||
const version = process.env.VERSION || packageJson.version;
|
||||
|
||||
analyticsClient.track(
|
||||
{
|
||||
event,
|
||||
anonymousId,
|
||||
properties: {
|
||||
...properties,
|
||||
platform: 'cli',
|
||||
},
|
||||
context: {
|
||||
app: {
|
||||
name: 'inso',
|
||||
version,
|
||||
},
|
||||
os: {
|
||||
name: getOsName(),
|
||||
version: os.release(),
|
||||
},
|
||||
},
|
||||
},
|
||||
() => {
|
||||
// Silently fail
|
||||
},
|
||||
);
|
||||
} catch {
|
||||
// Silently fail
|
||||
}
|
||||
analytics.track({ event, anonymousId, properties });
|
||||
} catch {}
|
||||
};
|
||||
|
||||
export const flushAnalytics = async (): Promise<void> => {
|
||||
try {
|
||||
await analyticsClient.closeAndFlush({ timeout: 5000 });
|
||||
} catch {
|
||||
// Silently fail
|
||||
}
|
||||
await analytics.closeAndFlush(5000);
|
||||
};
|
||||
|
||||
Binary file not shown.
@@ -5,8 +5,9 @@ import { test } from '../../playwright/test';
|
||||
test('can send requests', async ({ page, insomnia }) => {
|
||||
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
|
||||
|
||||
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
|
||||
const statusTag = page.getByTestId('response-pane').getByTestId('response-status-tag');
|
||||
const responseBody = page.getByTestId('response-pane');
|
||||
const responsePreviewBody = page.locator('[data-testid="response-pane"] >> [data-testid="CodeEditor"]:visible');
|
||||
|
||||
await insomnia.projectPage.importFixture('smoke-test-collection.yaml');
|
||||
|
||||
@@ -36,10 +37,10 @@ test('can send requests', async ({ page, insomnia }) => {
|
||||
.toBeVisible();
|
||||
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
|
||||
await expect.soft(statusTag).toContainText('200 OK');
|
||||
await expect.soft(responseBody).toContainText('"id": "1"');
|
||||
await expect.soft(responsePreviewBody).toContainText('"id": "1"');
|
||||
await page.getByRole('button', { name: 'Preview' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Raw Data' }).click();
|
||||
await expect.soft(responseBody).toContainText('{"id":"1"}');
|
||||
await expect.soft(responsePreviewBody).toContainText('{"id":"1"}');
|
||||
|
||||
await insomnia.navigationSidebar.clickRequestOrFolder('connects to event stream and shows ping response');
|
||||
await expect
|
||||
@@ -47,7 +48,7 @@ test('can send requests', async ({ page, insomnia }) => {
|
||||
.toBeVisible();
|
||||
await page.getByTestId('request-pane').getByRole('button', { name: 'Connect' }).click();
|
||||
await expect.soft(statusTag).toContainText('200 OK');
|
||||
await page.getByRole('tab', { name: 'Console' }).click();
|
||||
await page.getByTestId('response-pane').getByRole('tab', { name: 'Console' }).click();
|
||||
await expect.soft(responseBody).toContainText('Connected to 127.0.0.1');
|
||||
await page.getByTestId('request-pane').getByRole('button', { name: 'Disconnect' }).click();
|
||||
|
||||
@@ -61,7 +62,7 @@ test('can send requests', async ({ page, insomnia }) => {
|
||||
await expect.soft(statusTag).toContainText('200 OK');
|
||||
await page.getByRole('button', { name: 'Preview' }).click();
|
||||
await page.getByRole('menuitem', { name: 'Raw Data' }).click();
|
||||
await expect.soft(responseBody).toContainText('a,b,c');
|
||||
await expect.soft(responsePreviewBody).toContainText('a,b,c');
|
||||
|
||||
await insomnia.navigationSidebar.clickRequestOrFolder('sends dummy.xml request and shows raw response');
|
||||
await expect
|
||||
@@ -71,8 +72,8 @@ test('can send requests', async ({ page, insomnia }) => {
|
||||
.toBeVisible();
|
||||
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
|
||||
await expect.soft(statusTag).toContainText('200 OK');
|
||||
await expect.soft(responseBody).toContainText('xml version="1.0"');
|
||||
await expect.soft(responseBody).toContainText('<LoginResult>');
|
||||
await expect.soft(responsePreviewBody).toContainText('xml version="1.0"');
|
||||
await expect.soft(responsePreviewBody).toContainText('<LoginResult>');
|
||||
|
||||
await insomnia.navigationSidebar.clickRequestOrFolder('sends dummy.pdf request and shows rich response');
|
||||
await expect
|
||||
@@ -82,13 +83,34 @@ test('can send requests', async ({ page, insomnia }) => {
|
||||
.toBeVisible();
|
||||
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
|
||||
await expect.soft(statusTag).toContainText('200 OK');
|
||||
await page.getByRole('tab', { name: 'Console' }).click();
|
||||
|
||||
const pdfIframe = page.getByTestId('ResponsePDFView');
|
||||
await expect.soft(pdfIframe).toBeVisible();
|
||||
await expect.soft(pdfIframe).toHaveAttribute('src', /^blob:/);
|
||||
|
||||
// find Electron/Chromium's built-in PDF viewer extension
|
||||
await expect
|
||||
.poll(() => page.frames().some(f => f.url().startsWith('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai')), {
|
||||
timeout: 5000,
|
||||
message: 'Expected Chromium built-in PDF viewer extension frame to mount inside the PDF preview iframe',
|
||||
})
|
||||
.toBe(true);
|
||||
|
||||
await expect.soft(pdfIframe).toHaveScreenshot('dummy-pdf-preview.png', {
|
||||
animations: 'disabled',
|
||||
maxDiffPixelRatio: 0.15, // 15% discrepancy allowed for CI/environment differences
|
||||
timeout: 5000,
|
||||
});
|
||||
|
||||
await page.getByTestId('response-pane').getByRole('tab', { name: 'Console' }).click();
|
||||
await page.locator('pre').filter({ hasText: '< Content-Type: application/pdf' }).click();
|
||||
await page.getByTestId('response-pane').getByRole('tab', { name: 'Preview' }).click();
|
||||
|
||||
await insomnia.navigationSidebar.clickRequestOrFolder('sends request with basic authentication');
|
||||
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
|
||||
await expect.soft(statusTag).toContainText('200 OK');
|
||||
await expect.soft(responseBody).toContainText('basic auth received');
|
||||
await page.getByTestId('response-pane').getByRole('tab', { name: 'Preview' }).click();
|
||||
await expect.soft(responsePreviewBody).toContainText('basic auth received');
|
||||
|
||||
await insomnia.navigationSidebar.clickRequestOrFolder('sends request with cookie and get cookie in response');
|
||||
await expect
|
||||
@@ -96,7 +118,7 @@ test('can send requests', async ({ page, insomnia }) => {
|
||||
.toBeVisible();
|
||||
await page.getByTestId('request-pane').getByRole('button', { name: 'Send' }).click();
|
||||
await expect.soft(statusTag).toContainText('200 OK');
|
||||
await page.getByRole('tab', { name: 'Console' }).click();
|
||||
await page.getByTestId('response-pane').getByRole('tab', { name: 'Console' }).click();
|
||||
await expect.soft(responseBody).toContainText('Set-Cookie: insomnia-test-cookie=value123');
|
||||
|
||||
await insomnia.navigationSidebar.clickRequestOrFolder('delayed request');
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
@@ -64,7 +64,6 @@
|
||||
"@rjsf/utils": "6.0.0-beta.15",
|
||||
"@rjsf/validator-ajv8": "6.0.0-beta.15",
|
||||
"@seald-io/nedb": "^4.1.1",
|
||||
"@segment/analytics-node": "2.2.1",
|
||||
"@sentry/electron": "^6.5.0",
|
||||
"@stoplight/spectral-core": "^1.22.0",
|
||||
"@stoplight/spectral-formats": "^1.8.2",
|
||||
|
||||
@@ -66,8 +66,8 @@ vi.mock('../main/install-plugin', async () => {
|
||||
});
|
||||
|
||||
vi.mock('../main/analytics', () => ({
|
||||
trackSegmentEvent: vi.fn(),
|
||||
SegmentEvent: {
|
||||
trackAnalyticsEvent: vi.fn(),
|
||||
AnalyticsEvent: {
|
||||
installPlugin: 'Plugin Installed',
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -26,11 +26,7 @@ export async function absorbKey(sessionId: string, key: string) {
|
||||
getUserProfile({ sessionId: sessionIdResolved }),
|
||||
getEncryptionKeys({ sessionId: sessionIdResolved }),
|
||||
]);
|
||||
const {
|
||||
public_key: publicKey,
|
||||
enc_private_key: encPrivateKey,
|
||||
enc_symmetric_key: encSymmetricKey,
|
||||
} = keys;
|
||||
const { public_key: publicKey, enc_private_key: encPrivateKey, enc_symmetric_key: encSymmetricKey } = keys;
|
||||
const { email, id: accountId, first_name: firstName, last_name: lastName } = profile;
|
||||
const symmetricKeyStr = crypt.decryptAES(key, JSON.parse(encSymmetricKey));
|
||||
|
||||
@@ -67,7 +63,7 @@ export async function getPrivateKey() {
|
||||
}
|
||||
|
||||
export async function getCurrentSessionId() {
|
||||
const { id } = await services.userSession.getOrCreate();
|
||||
const { id } = await services.userSession.get();
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -122,16 +118,14 @@ export async function setSessionData(
|
||||
lastName,
|
||||
};
|
||||
|
||||
const userData = await services.userSession.getOrCreate();
|
||||
await services.userSession.update(userData, sessionData);
|
||||
await services.userSession.update(sessionData);
|
||||
|
||||
return sessionData;
|
||||
}
|
||||
|
||||
/** Update the session data with vault salt and vault key */
|
||||
export async function setVaultSessionData(vaultSalt: string, vaultKey: string) {
|
||||
const userData = await services.userSession.getOrCreate();
|
||||
await services.userSession.update(userData, { vaultSalt, vaultKey });
|
||||
await services.userSession.update({ vaultSalt, vaultKey });
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~ //
|
||||
@@ -139,25 +133,13 @@ export async function setVaultSessionData(vaultSalt: string, vaultKey: string) {
|
||||
// ~~~~~~~~~~~~~~~~ //
|
||||
|
||||
export async function getUserSession(): Promise<SessionData> {
|
||||
const userData = await services.userSession.getOrCreate();
|
||||
const userData = await services.userSession.get();
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
async function _unsetSessionData() {
|
||||
await services.userSession.getOrCreate();
|
||||
await services.userSession.update(await services.userSession.getOrCreate(), {
|
||||
id: '',
|
||||
accountId: '',
|
||||
email: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
symmetricKey: {} as JsonWebKey,
|
||||
publicKey: {} as JsonWebKey,
|
||||
encPrivateKey: {} as crypt.AESMessage,
|
||||
vaultSalt: '',
|
||||
vaultKey: '',
|
||||
});
|
||||
await services.userSession.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,12 +254,12 @@ export async function migrateFromLocalStorage() {
|
||||
try {
|
||||
const sessionData = JSON.parse(session) as SessionData;
|
||||
|
||||
const currentUserSession = await services.userSession.getOrCreate();
|
||||
const currentUserSession = await services.userSession.get();
|
||||
|
||||
if (currentUserSession.id) {
|
||||
console.warn('Session already exists, skipping migration');
|
||||
} else {
|
||||
await services.userSession.update(currentUserSession, sessionData);
|
||||
await services.userSession.update(sessionData);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse session data', e);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { getRenderedRequestAndContext } from '../render';
|
||||
describe('export', () => {
|
||||
beforeEach(async () => {
|
||||
await db.init({ inMemoryOnly: true }, true);
|
||||
await services.project.all();
|
||||
await services.project.list();
|
||||
await services.settings.getOrCreate();
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ const reqGroupBuilder = createBuilder(requestGroupModelSchema);
|
||||
|
||||
describe('render tests', () => {
|
||||
beforeEach(async () => {
|
||||
await services.project.all();
|
||||
await services.settings.getOrCreate();
|
||||
envBuilder.reset();
|
||||
reqGroupBuilder.reset();
|
||||
|
||||
@@ -592,7 +592,7 @@ export const importResourcesToNewWorkspace = async ({
|
||||
}): Promise<Workspace> => {
|
||||
invariant(resourceCacheItem, 'No resources to import');
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
const resources = resourceCacheItem.resources;
|
||||
@@ -766,8 +766,7 @@ export async function findExistingImportedSpec(
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
const allProjects = await services.project.all();
|
||||
const filteredProjects = organizationId ? allProjects.filter(p => p.parentId === organizationId) : allProjects;
|
||||
const filteredProjects = await services.project.list({ organizationId });
|
||||
|
||||
// match active project first, then look in rest
|
||||
const projectIds = new Set<string>();
|
||||
|
||||
@@ -34,7 +34,7 @@ export async function fetchAndCacheOrganizationStorageRule(
|
||||
}
|
||||
}
|
||||
|
||||
const { id: sessionId } = await services.userSession.getOrCreate();
|
||||
const { id: sessionId } = await services.userSession.get();
|
||||
|
||||
return await getOrganizationStorageRule({
|
||||
organizationId,
|
||||
|
||||
@@ -129,6 +129,9 @@ export async function buildRenderContext({
|
||||
const keys = _getOrderedEnvironmentKeys(subObject);
|
||||
|
||||
for (const key of keys) {
|
||||
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* If we're overwriting a string, try to render it first using the same key from the base
|
||||
* environment to support same-variable recursion. This allows for the following scenario:
|
||||
|
||||
@@ -21,7 +21,7 @@ import { registerLLMConfigServiceAPI } from '~/main/llm-config-service';
|
||||
import { userDataFolder } from '../config/config.json';
|
||||
import { getAppVersion, getProductName, isDevelopment } from './common/constants';
|
||||
import { isMac } from './common/platform';
|
||||
import { SegmentEvent, trackSegmentEvent } from './main/analytics';
|
||||
import { AnalyticsEvent, trackAnalyticsEvent } from './main/analytics';
|
||||
import { registerInsomniaProtocols } from './main/api.protocol';
|
||||
import { backupIfNewerVersionAvailable } from './main/backup';
|
||||
import { registerSyncHandlers } from './main/cloud-sync/ipc';
|
||||
@@ -289,7 +289,7 @@ async function _createModelInstances() {
|
||||
await services.stats.get();
|
||||
await services.settings.getOrCreate();
|
||||
try {
|
||||
const scratchpadProject = await services.project.getById(models.project.SCRATCHPAD_PROJECT_ID);
|
||||
const scratchpadProject = await services.project.get(models.project.SCRATCHPAD_PROJECT_ID);
|
||||
const scratchPad = await services.workspace.getById(models.workspace.SCRATCHPAD_WORKSPACE_ID);
|
||||
if (!scratchpadProject) {
|
||||
console.log('[main] Initializing Scratch Pad Project');
|
||||
@@ -365,7 +365,7 @@ async function _trackStats() {
|
||||
|
||||
const settings = await services.settings.get();
|
||||
|
||||
trackSegmentEvent(SegmentEvent.appStarted, {
|
||||
trackAnalyticsEvent(AnalyticsEvent.appStarted, {
|
||||
localProjects,
|
||||
remoteProjects,
|
||||
createdRequests: stats.createdRequests,
|
||||
|
||||
@@ -289,7 +289,7 @@ const main: Window['main'] = {
|
||||
secretStorage,
|
||||
electronStorage,
|
||||
sync,
|
||||
trackSegmentEvent: options => ipcRenderer.send('trackSegmentEvent', options),
|
||||
trackAnalyticsEvent: options => ipcRenderer.send('trackAnalyticsEvent', options),
|
||||
trackPageView: options => ipcRenderer.send('trackPageView', options),
|
||||
setCurrentOrganizationId: organizationId => ipcRenderer.send('analytics.setOrganizationId', organizationId),
|
||||
showNunjucksContextMenu: options => ipcRenderer.send('show-nunjucks-context-menu', options),
|
||||
|
||||
@@ -30,7 +30,6 @@ import { services } from '~/insomnia-data';
|
||||
// @vitest-environment jsdom
|
||||
describe('Request Model - Comprehensive Tests', () => {
|
||||
beforeEach(async () => {
|
||||
await services.project.all();
|
||||
await services.settings.getOrCreate();
|
||||
|
||||
// Create test project for all tests
|
||||
|
||||
@@ -15,6 +15,7 @@ import * as mcpResponseService from './mcp-response';
|
||||
import * as mockRouteService from './mock-route';
|
||||
import * as mockServerService from './mock-server';
|
||||
import * as oAuth2TokenService from './o-auth-2-token';
|
||||
import * as organizationService from './organization';
|
||||
import * as pluginDataService from './plugin-data';
|
||||
import * as projectService from './project';
|
||||
import * as protoDirectoryService from './proto-directory';
|
||||
@@ -59,6 +60,7 @@ export const servicesNodeImpl = {
|
||||
mcpRequest: mcpRequestService,
|
||||
mcpResponse: mcpResponseService,
|
||||
oAuth2Token: oAuth2TokenService,
|
||||
organization: organizationService,
|
||||
pluginData: pluginDataService,
|
||||
protoDirectory: protoDirectoryService,
|
||||
protoFile: protoFileService,
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { getOrganizations, type Organization } from 'insomnia-api';
|
||||
|
||||
import { models } from '~/insomnia-data';
|
||||
|
||||
import * as userSessionService from './user-session';
|
||||
|
||||
function sortOrganizations(accountId: string, organizations: Organization[]): Organization[] {
|
||||
const home = organizations.find(
|
||||
organization =>
|
||||
models.organization.isPersonalOrganization(organization) &&
|
||||
models.organization.isOwnerOfOrganization({
|
||||
organization,
|
||||
accountId,
|
||||
}),
|
||||
);
|
||||
const myOrgs = organizations
|
||||
.filter(
|
||||
organization =>
|
||||
!models.organization.isPersonalOrganization(organization) &&
|
||||
models.organization.isOwnerOfOrganization({
|
||||
organization,
|
||||
accountId,
|
||||
}),
|
||||
)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
const notMyOrgs = organizations
|
||||
.filter(
|
||||
organization =>
|
||||
!models.organization.isOwnerOfOrganization({
|
||||
organization,
|
||||
accountId,
|
||||
}),
|
||||
)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return [...(home ? [home] : []), ...myOrgs, ...notMyOrgs];
|
||||
}
|
||||
|
||||
/**
|
||||
* List organizations from the Insomnia cloud API.
|
||||
*/
|
||||
export async function list(): Promise<Organization[]> {
|
||||
const { id: sessionId, accountId } = await userSessionService.get();
|
||||
|
||||
if (!sessionId || !accountId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = await getOrganizations({ sessionId });
|
||||
const organizations = result?.organizations ?? [];
|
||||
|
||||
return sortOrganizations(accountId, organizations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single organization by ID.
|
||||
*/
|
||||
export async function get(id: string): Promise<Organization | undefined> {
|
||||
const all = await list();
|
||||
return all.find(org => org.id === id);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Project } from '~/insomnia-data';
|
||||
import type { Project, Query } from '~/insomnia-data';
|
||||
import { database as db, models } from '~/insomnia-data';
|
||||
|
||||
const { type } = models.project;
|
||||
@@ -7,30 +7,42 @@ export function create(patch: Partial<Project> = {}) {
|
||||
return db.docCreate<Project>(type, patch);
|
||||
}
|
||||
|
||||
export function getById(_id: string) {
|
||||
return db.findOne<Project>(type, { _id });
|
||||
export function list(options?: { gitRepositoryIds?: string[]; organizationId?: string }) {
|
||||
const query: Query<Project> = {};
|
||||
if (options?.organizationId) {
|
||||
query.parentId = options.organizationId;
|
||||
}
|
||||
if (options?.gitRepositoryIds) {
|
||||
const queryIds = options.gitRepositoryIds.flatMap(id => models.project.getQueryableGitRepositoryIds(id));
|
||||
query.gitRepositoryId = { $in: queryIds };
|
||||
}
|
||||
return db.find<Project>(type, query);
|
||||
}
|
||||
|
||||
export function get(id: string) {
|
||||
return db.findOne<Project>(type, { _id: id });
|
||||
}
|
||||
|
||||
export function getByRemoteId(remoteId: string) {
|
||||
return db.findOne<Project>(type, { remoteId });
|
||||
}
|
||||
|
||||
export function getAllByGitRepositoryIds(gitRepositoryIds: string[]) {
|
||||
const queryIds = gitRepositoryIds.flatMap(id => models.project.getQueryableGitRepositoryIds(id));
|
||||
return db.find<Project>(type, {
|
||||
gitRepositoryId: { $in: queryIds },
|
||||
});
|
||||
}
|
||||
const getProjectByIdOrProject = async (idOrProject: string | Project) => {
|
||||
const project = typeof idOrProject === 'string' ? await get(idOrProject) : idOrProject;
|
||||
if (!project) {
|
||||
throw new Error(
|
||||
`Project not found: ${typeof idOrProject === 'string' ? idOrProject : `_id=${idOrProject._id}, name=${idOrProject.name}`}`,
|
||||
);
|
||||
}
|
||||
return project;
|
||||
};
|
||||
|
||||
export function remove(project: Project) {
|
||||
return db.remove(project);
|
||||
}
|
||||
|
||||
export function update(project: Project, patch: Partial<Project>) {
|
||||
export async function update(idOrProject: string | Project, patch: Partial<Project>) {
|
||||
const project = await getProjectByIdOrProject(idOrProject);
|
||||
return db.docUpdate(project, patch);
|
||||
}
|
||||
|
||||
export async function all() {
|
||||
const projects = await db.find<Project>(type);
|
||||
return projects;
|
||||
export async function remove(idOrProject: string | Project) {
|
||||
const project = await getProjectByIdOrProject(idOrProject);
|
||||
return db.remove(project);
|
||||
}
|
||||
|
||||
@@ -3,41 +3,23 @@ import { database as db, models } from '~/insomnia-data';
|
||||
|
||||
const { type } = models.userSession;
|
||||
|
||||
export async function all() {
|
||||
let userList = await db.find<UserSession>(type);
|
||||
|
||||
if (userList?.length === 0) {
|
||||
userList = [await getOrCreate()];
|
||||
}
|
||||
|
||||
return userList;
|
||||
}
|
||||
|
||||
async function create() {
|
||||
const user = await db.docCreate<UserSession>(type);
|
||||
return user;
|
||||
}
|
||||
|
||||
export async function update(user: UserSession, patch: Partial<UserSession>) {
|
||||
const updatedUser = await db.docUpdate<UserSession>(user, patch);
|
||||
return updatedUser;
|
||||
}
|
||||
|
||||
export async function patch(patch: Partial<UserSession>) {
|
||||
const user = await getOrCreate();
|
||||
const updatedUser = await db.docUpdate<UserSession>(user, patch);
|
||||
return updatedUser;
|
||||
}
|
||||
|
||||
export async function getOrCreate() {
|
||||
export async function get() {
|
||||
const result = await db.findOne<UserSession>(type);
|
||||
|
||||
if (!result) {
|
||||
return await create();
|
||||
const user = await db.docCreate<UserSession>(type);
|
||||
return user;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function get() {
|
||||
return getOrCreate();
|
||||
export async function update(patch: Partial<UserSession>) {
|
||||
const user = await get();
|
||||
const updatedUser = await db.docUpdate<UserSession>(user, patch);
|
||||
return updatedUser;
|
||||
}
|
||||
|
||||
export async function remove() {
|
||||
const user = await get();
|
||||
await db.remove(user);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Tests run against the in-memory NeDB initialized by setup-vitest.ts.
|
||||
* fetch is mocked per-test to return shaped Konnect API responses.
|
||||
* window.main is stubbed globally so trackSegmentEvent calls don't throw.
|
||||
* window.main is stubbed globally so trackAnalyticsEvent calls don't throw.
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
@@ -101,13 +101,13 @@ const konnectRequests = (docs: any[]) => docs.filter((r: any) => r.konnectRouteK
|
||||
const konnectWorkspaces = (docs: any[]) => docs.filter((w: any) => w.konnectServiceId != null);
|
||||
const konnectProjects = (docs: any[]) => docs.filter((p: any) => p.konnectControlPlaneId != null);
|
||||
|
||||
const trackSegmentEvent = vi.fn();
|
||||
const trackAnalyticsEvent = vi.fn();
|
||||
|
||||
beforeEach(async () => {
|
||||
// Re-init with fresh in-memory NeDB buckets — clean slate for every test.
|
||||
await initDatabase(mainDatabase, { inMemoryOnly: true }, true);
|
||||
resetV4Counter();
|
||||
vi.stubGlobal('window', { main: { trackSegmentEvent } });
|
||||
vi.stubGlobal('window', { main: { trackAnalyticsEvent } });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -20,10 +20,10 @@ vi.mock('~/insomnia-data', () => ({
|
||||
getById: vi.fn(),
|
||||
},
|
||||
project: {
|
||||
getById: vi.fn(),
|
||||
get: vi.fn(),
|
||||
},
|
||||
userSession: {
|
||||
getOrCreate: vi.fn(),
|
||||
get: vi.fn(),
|
||||
},
|
||||
workspaceMeta: {
|
||||
getOrCreateByParentId: vi.fn(),
|
||||
@@ -68,8 +68,8 @@ describe('sync-initialization', () => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
vi.mocked(services.workspace.getById).mockResolvedValue(workspace);
|
||||
vi.mocked(services.project.getById).mockResolvedValue(project);
|
||||
vi.mocked(services.userSession.getOrCreate).mockResolvedValue({ id: 'sess_123' } as any);
|
||||
vi.mocked(services.project.get).mockResolvedValue(project);
|
||||
vi.mocked(services.userSession.get).mockResolvedValue({ id: 'sess_123' } as any);
|
||||
vi.mocked(services.workspaceMeta.getOrCreateByParentId).mockResolvedValue({ gitRepositoryId: null } as any);
|
||||
vi.mocked(services.environment.getOrCreateForParentId).mockResolvedValue({} as any);
|
||||
vi.mocked(services.cookieJar.getOrCreateForParentId).mockResolvedValue({} as any);
|
||||
@@ -83,7 +83,7 @@ describe('sync-initialization', () => {
|
||||
});
|
||||
|
||||
it('returns early when initializing a workspace backend project without a session', async () => {
|
||||
vi.mocked(services.userSession.getOrCreate).mockResolvedValue({ id: null } as any);
|
||||
vi.mocked(services.userSession.get).mockResolvedValue({ id: null } as any);
|
||||
|
||||
await initializeWorkspaceBackendProject({ workspaceId: workspace._id });
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import crypto from 'node:crypto';
|
||||
|
||||
import { Analytics } from '@segment/analytics-node';
|
||||
import * as Sentry from '@sentry/electron/main';
|
||||
import { net } from 'electron';
|
||||
import { AnalyticsEvent, InsomniaAnalytics } from 'insomnia-analytics';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { services } from '~/insomnia-data';
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
getSegmentWriteKey,
|
||||
PLAYWRIGHT_TEST,
|
||||
} from '../common/constants';
|
||||
import { platform } from '../common/platform';
|
||||
|
||||
export { AnalyticsEvent };
|
||||
|
||||
let _currentOrganizationId: string | undefined;
|
||||
|
||||
@@ -23,18 +24,29 @@ export function setCurrentOrganizationId(id: string | undefined): void {
|
||||
_currentOrganizationId = id;
|
||||
}
|
||||
|
||||
const analytics = new Analytics({
|
||||
const analytics = new InsomniaAnalytics({
|
||||
writeKey: getSegmentWriteKey(),
|
||||
httpClient: {
|
||||
makeRequest(_options) {
|
||||
return net.fetch(_options.url, {
|
||||
method: _options.method,
|
||||
headers: _options.headers,
|
||||
body: _options.body,
|
||||
signal: AbortSignal.timeout(_options.httpRequestTimeout),
|
||||
});
|
||||
app: {
|
||||
appName: getProductName(),
|
||||
appVersion: getAppVersion(),
|
||||
osVersion: () => process.getSystemVersion(),
|
||||
platform: 'app',
|
||||
},
|
||||
settings: {
|
||||
httpClient: {
|
||||
makeRequest(_options) {
|
||||
return net.fetch(_options.url, {
|
||||
method: _options.method,
|
||||
headers: _options.headers,
|
||||
body: _options.body,
|
||||
signal: AbortSignal.timeout(_options.httpRequestTimeout),
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
onError: error => {
|
||||
console.warn('[analytics] Error sending analytics event', error);
|
||||
},
|
||||
});
|
||||
|
||||
const getDeviceId = async () => {
|
||||
@@ -42,97 +54,48 @@ const getDeviceId = async () => {
|
||||
return settings.deviceId || (await services.settings.update(settings, { deviceId: uuidv4() })).deviceId;
|
||||
};
|
||||
|
||||
export enum SegmentEvent {
|
||||
appStarted = 'App Started',
|
||||
collectionCreate = 'Collection Created',
|
||||
dataExport = 'Data Exported',
|
||||
dataImport = 'Data Imported',
|
||||
loginSuccess = 'Login Success',
|
||||
documentCreate = 'Document Created',
|
||||
kongConnected = 'Kong Connected',
|
||||
kongSync = 'Kong Synced',
|
||||
requestBodyTypeSelect = 'Request Body Type Selected',
|
||||
requestCreated = 'Request Created',
|
||||
requestExecuted = 'Request Executed',
|
||||
collectionRunExecute = 'Collection Run Executed',
|
||||
projectLocalCreate = 'Local Project Created',
|
||||
projectLocalDelete = 'Local Project Deleted',
|
||||
testSuiteCreate = 'Test Suite Created',
|
||||
testSuiteDelete = 'Test Suite Deleted',
|
||||
unitTestCreate = 'Unit Test Created',
|
||||
unitTestDelete = 'Unit Test Deleted',
|
||||
unitTestRun = 'Ran Individual Unit Test',
|
||||
unitTestRunAll = 'Ran All Unit Tests',
|
||||
vcsSyncStart = 'VCS Sync Started',
|
||||
vcsSyncComplete = 'VCS Sync Completed',
|
||||
vcsAction = 'VCS Action Executed',
|
||||
gitAuthenticationCompleted = 'Git Authentication Completed',
|
||||
gitAuthenticationUpdated = 'Git Authentication Updated',
|
||||
buttonClick = 'Button Clicked',
|
||||
aiFeatureEnabled = 'AI Feature Enabled',
|
||||
aiFeatureDisabled = 'AI Feature Disabled',
|
||||
mcpClientConnected = 'MCP Client Connected',
|
||||
mcpClientDisconnected = 'MCP Client Disconnected',
|
||||
mcpToolCalled = 'MCP Tool Called',
|
||||
mcpResourceRead = 'MCP Resource Read',
|
||||
mcpPromptCalled = 'MCP Prompt Called',
|
||||
installPlugin = 'Plugin Installed',
|
||||
}
|
||||
|
||||
function hashString(input: string) {
|
||||
return crypto.createHash('sha256').update(input).digest('hex');
|
||||
}
|
||||
|
||||
export async function trackSegmentEvent(event: SegmentEvent, properties?: Record<string, any>) {
|
||||
export async function trackAnalyticsEvent(event: AnalyticsEvent, properties?: Record<string, any>) {
|
||||
if (PLAYWRIGHT_TEST) {
|
||||
return;
|
||||
}
|
||||
const settings = await services.settings.getOrCreate();
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
if (!userSession?.hashedAccountId) {
|
||||
userSession.hashedAccountId = userSession?.accountId ? hashString(userSession.accountId) : '';
|
||||
}
|
||||
const allowAnalytics = settings.enableAnalytics || userSession?.hashedAccountId;
|
||||
if (allowAnalytics) {
|
||||
try {
|
||||
const anonymousId = (await getDeviceId()) ?? '';
|
||||
const context = {
|
||||
app: { name: getProductName(), version: getAppVersion() },
|
||||
os: { name: _getOsName(), version: process.getSystemVersion() },
|
||||
};
|
||||
if (!allowAnalytics) {
|
||||
return;
|
||||
}
|
||||
|
||||
analytics.track(
|
||||
{
|
||||
event,
|
||||
properties: {
|
||||
...(_currentOrganizationId && { organization_id: _currentOrganizationId }),
|
||||
...properties,
|
||||
platform: 'app',
|
||||
},
|
||||
context,
|
||||
anonymousId,
|
||||
userId: userSession?.hashedAccountId || '',
|
||||
try {
|
||||
const anonymousId = (await getDeviceId()) ?? '';
|
||||
analytics.track({
|
||||
event,
|
||||
properties: {
|
||||
...(_currentOrganizationId && { organization_id: _currentOrganizationId }),
|
||||
...properties,
|
||||
},
|
||||
anonymousId,
|
||||
userId: userSession?.hashedAccountId || '',
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
console.warn('[analytics] Unexpected error while sending analytics event', error);
|
||||
} finally {
|
||||
if (!userSession?.hashedAccountId && [AnalyticsEvent.unitTestRun, AnalyticsEvent.unitTestRunAll].includes(event)) {
|
||||
Sentry.captureException(`Run tests by anonymous`, {
|
||||
tags: {
|
||||
source: 'main/analytics',
|
||||
},
|
||||
error => {
|
||||
if (error) {
|
||||
console.warn('[analytics] Error sending segment event', error);
|
||||
}
|
||||
extra: {
|
||||
organizationId: properties?.organizationId || '',
|
||||
projectId: properties?.projectId || '',
|
||||
},
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
console.warn('[analytics] Unexpected error while sending segment event', error);
|
||||
} finally {
|
||||
if (!userSession?.hashedAccountId && [SegmentEvent.unitTestRun, SegmentEvent.unitTestRunAll].includes(event)) {
|
||||
Sentry.captureException(`Run tests by anonymous`, {
|
||||
tags: {
|
||||
source: 'main/analytics',
|
||||
},
|
||||
extra: {
|
||||
organizationId: properties?.organizationId || '',
|
||||
projectId: properties?.projectId || '',
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,54 +105,30 @@ export async function trackPageView(name: string) {
|
||||
return;
|
||||
}
|
||||
const settings = await services.settings.getOrCreate();
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
if (!userSession?.hashedAccountId) {
|
||||
userSession.hashedAccountId = userSession?.accountId ? hashString(userSession.accountId) : '';
|
||||
}
|
||||
|
||||
const allowAnalytics = settings.enableAnalytics || userSession?.hashedAccountId;
|
||||
if (allowAnalytics) {
|
||||
try {
|
||||
const anonymousId = (await getDeviceId()) ?? '';
|
||||
const context = {
|
||||
app: { name: getProductName(), version: getAppVersion() },
|
||||
os: { name: _getOsName(), version: process.getSystemVersion() },
|
||||
};
|
||||
if (!allowAnalytics) {
|
||||
return;
|
||||
}
|
||||
|
||||
analytics.page({ name, context, anonymousId, userId: userSession?.hashedAccountId }, error => {
|
||||
if (error) {
|
||||
console.warn('[analytics] Error sending segment event', error);
|
||||
}
|
||||
try {
|
||||
const anonymousId = (await getDeviceId()) ?? '';
|
||||
analytics.page({ name, anonymousId, userId: userSession?.hashedAccountId });
|
||||
|
||||
if (userSession?.id) {
|
||||
net.fetch(getApiBaseURL() + '/v1/telemetry/', {
|
||||
method: 'POST',
|
||||
headers: new Headers({
|
||||
'X-Session-Id': userSession?.id,
|
||||
'X-Insomnia-Client': getClientString(),
|
||||
}),
|
||||
});
|
||||
|
||||
if (userSession?.id) {
|
||||
net.fetch(getApiBaseURL() + '/v1/telemetry/', {
|
||||
method: 'POST',
|
||||
headers: new Headers({
|
||||
'X-Session-Id': userSession?.id,
|
||||
'X-Insomnia-Client': getClientString(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.warn('[analytics] Unexpected error while sending segment event', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~ //
|
||||
// Private Functions //
|
||||
// ~~~~~~~~~~~~~~~~~ //
|
||||
function _getOsName() {
|
||||
switch (platform) {
|
||||
case 'darwin': {
|
||||
return 'mac';
|
||||
}
|
||||
case 'win32': {
|
||||
return 'windows';
|
||||
}
|
||||
default: {
|
||||
return platform;
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.warn('[analytics] Unexpected error while sending analytics event', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export const initializeWorkspaceBackendProject = async ({ workspaceId }: { works
|
||||
const workspace = await services.workspace.getById(workspaceId);
|
||||
invariant(workspace, 'Workspace not found');
|
||||
|
||||
const { id } = await services.userSession.getOrCreate();
|
||||
const { id } = await services.userSession.get();
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
@@ -32,10 +32,10 @@ export const syncNewWorkspaceIfNeeded = async ({ workspaceId }: { workspaceId: s
|
||||
const workspace = await services.workspace.getById(workspaceId);
|
||||
invariant(workspace, 'Workspace not found');
|
||||
|
||||
const project = await services.project.getById(workspace.parentId);
|
||||
const project = await services.project.get(workspace.parentId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
if (!userSession.id || !models.project.isRemoteProject(project)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ import { routableFSClient } from '../sync/git/routable-fs-client';
|
||||
import { shallowClone } from '../sync/git/shallow-clone';
|
||||
import type { AutoResolvedConflict, MergeConflict } from '../sync/types';
|
||||
import { invariant } from '../utils/invariant';
|
||||
import { SegmentEvent, trackSegmentEvent } from './analytics';
|
||||
import { AnalyticsEvent, trackAnalyticsEvent } from './analytics';
|
||||
import { ipcMainHandle } from './ipc/electron';
|
||||
|
||||
// Initialize Git Remote Providers on module load
|
||||
@@ -153,7 +153,7 @@ export function getErrorMessage(error: unknown): string {
|
||||
// Non-Error objects
|
||||
return 'Unknown Error';
|
||||
}
|
||||
export function vcsSegmentEventProperties(type: 'git', action: VCSAction, error?: string) {
|
||||
export function vcsEventProperties(type: 'git', action: VCSAction, error?: string) {
|
||||
return { type, action, error };
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ async function getGitRepository({ projectId, workspaceId }: { projectId: string;
|
||||
}
|
||||
|
||||
invariant(projectId, 'Project ID is required');
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
invariant(models.project.isConnectedGitProject(project), 'Project is not linked to a git repository');
|
||||
const repoId = models.project.getEffectiveRepoId(project);
|
||||
@@ -290,7 +290,7 @@ export async function getProjectGitFileIssues({
|
||||
workspaceId,
|
||||
gitRepositoryId,
|
||||
}: GetProjectGitFileIssuesOptions): Promise<WorkspaceFileIssue[]> {
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
if (!project || !models.project.isConnectedGitProject(project)) {
|
||||
return [];
|
||||
}
|
||||
@@ -1097,8 +1097,8 @@ export const cloneGitRepoAction = async ({
|
||||
}
|
||||
|
||||
if (!projectId) {
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncStart, {
|
||||
...vcsSegmentEventProperties('git', 'clone'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsSyncStart, {
|
||||
...vcsEventProperties('git', 'clone'),
|
||||
provider,
|
||||
repoId: repoSettingsPatch._id,
|
||||
});
|
||||
@@ -1157,7 +1157,7 @@ export const cloneGitRepoAction = async ({
|
||||
|
||||
async function getProject() {
|
||||
if (cloneIntoProjectId) {
|
||||
const project = await services.project.getById(cloneIntoProjectId);
|
||||
const project = await services.project.get(cloneIntoProjectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
await services.project.update(project, {
|
||||
@@ -1230,8 +1230,8 @@ export const cloneGitRepoAction = async ({
|
||||
});
|
||||
|
||||
await database.flushChanges(bufferId);
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
|
||||
...vcsSegmentEventProperties('git', 'clone'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
|
||||
...vcsEventProperties('git', 'clone'),
|
||||
providerName,
|
||||
repoId: repoSettingsPatch._id,
|
||||
});
|
||||
@@ -1242,11 +1242,11 @@ export const cloneGitRepoAction = async ({
|
||||
};
|
||||
}
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncStart, {
|
||||
...vcsSegmentEventProperties('git', 'clone'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsSyncStart, {
|
||||
...vcsEventProperties('git', 'clone'),
|
||||
provider,
|
||||
repoId: repoSettingsPatch._id,
|
||||
});
|
||||
@@ -1304,8 +1304,8 @@ export const cloneGitRepoAction = async ({
|
||||
});
|
||||
await services.apiSpec.getOrCreateForParentId(workspace._id);
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
|
||||
...vcsSegmentEventProperties('git', 'clone', 'no directory found'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
|
||||
...vcsEventProperties('git', 'clone', 'no directory found'),
|
||||
providerName: provider,
|
||||
repoId: repoSettingsPatch._id,
|
||||
});
|
||||
@@ -1323,8 +1323,8 @@ export const cloneGitRepoAction = async ({
|
||||
const workspaces = await inMemoryFsClient.promises.readdir(workspaceBase);
|
||||
|
||||
if (workspaces.length === 0) {
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
|
||||
...vcsSegmentEventProperties('git', 'clone', 'no workspaces found'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
|
||||
...vcsEventProperties('git', 'clone', 'no workspaces found'),
|
||||
providerName: provider,
|
||||
repoId: repoSettingsPatch._id,
|
||||
});
|
||||
@@ -1335,8 +1335,8 @@ export const cloneGitRepoAction = async ({
|
||||
}
|
||||
|
||||
if (workspaces.length > 1) {
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
|
||||
...vcsSegmentEventProperties('git', 'clone', 'multiple workspaces found'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
|
||||
...vcsEventProperties('git', 'clone', 'multiple workspaces found'),
|
||||
providerName: provider,
|
||||
repoId: repoSettingsPatch._id,
|
||||
});
|
||||
@@ -1359,7 +1359,7 @@ export const cloneGitRepoAction = async ({
|
||||
const existingWorkspace = await services.workspace.getById(workspace._id);
|
||||
|
||||
if (existingWorkspace) {
|
||||
const project = await services.project.getById(existingWorkspace.parentId);
|
||||
const project = await services.project.get(existingWorkspace.parentId);
|
||||
if (!project) {
|
||||
return {
|
||||
errors: [
|
||||
@@ -1424,8 +1424,8 @@ export const cloneGitRepoAction = async ({
|
||||
|
||||
// Flush DB changes
|
||||
await database.flushChanges(bufferId);
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, {
|
||||
...vcsSegmentEventProperties('git', 'clone'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsSyncComplete, {
|
||||
...vcsEventProperties('git', 'clone'),
|
||||
providerName: provider,
|
||||
repoId: repoSettingsPatch._id,
|
||||
});
|
||||
@@ -1469,7 +1469,7 @@ export const updateGitRepoAction = async ({
|
||||
const workspaceMeta = await services.workspaceMeta.getByParentId(workspaceId);
|
||||
gitRepositoryId = workspaceMeta?.gitRepositoryId;
|
||||
} else if (projectId) {
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
gitRepositoryId = project.gitRepositoryId;
|
||||
}
|
||||
@@ -1497,7 +1497,7 @@ export const updateGitRepoAction = async ({
|
||||
gitRepositoryId: gitRepository._id,
|
||||
});
|
||||
} else if (projectId) {
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
await services.project.update(project, {
|
||||
gitRepositoryId: models.project.toProtectedRepoId(gitRepository._id),
|
||||
@@ -1557,7 +1557,7 @@ export const resetGitRepoAction = async ({ projectId, workspaceId }: { projectId
|
||||
gitRepositoryId: null,
|
||||
});
|
||||
} else if (projectId) {
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
await services.project.update(project, {
|
||||
gitRepositoryId: models.project.EMPTY_GIT_PROJECT_ID,
|
||||
@@ -1601,8 +1601,8 @@ export const commitToGitRepoAction = async ({
|
||||
providerName = credentials.provider;
|
||||
}
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'commit'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'commit'),
|
||||
providerName,
|
||||
repoId: gitRepository._id,
|
||||
});
|
||||
@@ -1730,8 +1730,8 @@ export const commitAndPushToGitRepoAction = async ({
|
||||
providerName = credentials.provider;
|
||||
}
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'commit'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'commit'),
|
||||
providerName,
|
||||
repoId: repo._id,
|
||||
});
|
||||
@@ -1770,8 +1770,8 @@ export const commitAndPushToGitRepoAction = async ({
|
||||
try {
|
||||
await GitVCS.push(repo.credentialsId);
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'push'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'push'),
|
||||
providerName,
|
||||
repoId: repo._id,
|
||||
});
|
||||
@@ -1804,8 +1804,8 @@ export const commitAndPushToGitRepoAction = async ({
|
||||
}
|
||||
const errorMessage = getErrorMessage(err);
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'push', errorMessage),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'push', errorMessage),
|
||||
providerName,
|
||||
repoId: repo._id,
|
||||
});
|
||||
@@ -1846,8 +1846,8 @@ export const createNewGitBranchAction = async ({
|
||||
providerName = credentials.provider;
|
||||
}
|
||||
await GitVCS.checkout(branch);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'create_branch'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'create_branch'),
|
||||
providerName,
|
||||
repoId: gitRepository._id,
|
||||
});
|
||||
@@ -2021,8 +2021,8 @@ export const mergeGitBranch = async ({
|
||||
await repoFileWatcherRegistry.importAllFiles(gitRepoId);
|
||||
clearConflictSuppression(gitRepository._id);
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'merge_branch'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'merge_branch'),
|
||||
providerName,
|
||||
repoId: gitRepository._id,
|
||||
});
|
||||
@@ -2050,8 +2050,8 @@ export const mergeGitBranch = async ({
|
||||
errorMessage = `${err.message}, ${err.data.response}`;
|
||||
}
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'merge_branch', errorMessage),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'merge_branch', errorMessage),
|
||||
providerName,
|
||||
repoId: gitRepository._id,
|
||||
});
|
||||
@@ -2086,8 +2086,8 @@ export const deleteGitBranchAction = async ({
|
||||
providerName = credentials.provider;
|
||||
}
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'delete_branch'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'delete_branch'),
|
||||
providerName,
|
||||
repoId: repo._id,
|
||||
});
|
||||
@@ -2163,8 +2163,8 @@ export const pushToGitRemoteAction = async ({
|
||||
const bufferId = await database.bufferChanges();
|
||||
await GitVCS.push(gitRepository.credentialsId);
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', force ? 'force_push' : 'push'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', force ? 'force_push' : 'push'),
|
||||
providerName,
|
||||
repoId: gitRepository._id,
|
||||
});
|
||||
@@ -2208,8 +2208,8 @@ export const pushToGitRemoteAction = async ({
|
||||
}
|
||||
const errorMessage = getErrorMessage(err);
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'push', errorMessage),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'push', errorMessage),
|
||||
providerName,
|
||||
repoId: gitRepository._id,
|
||||
});
|
||||
@@ -2277,8 +2277,8 @@ export async function pullFromGitRemote({ projectId, workspaceId }: { projectId:
|
||||
await repoFileWatcherRegistry.importAllFiles(gitRepository._id);
|
||||
clearConflictSuppression(repoId);
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'pull'),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'pull'),
|
||||
providerName: credentials.provider,
|
||||
repoId: gitRepository._id,
|
||||
});
|
||||
@@ -2330,8 +2330,8 @@ export async function pullFromGitRemote({ projectId, workspaceId }: { projectId:
|
||||
providerName = credentials.provider;
|
||||
}
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, {
|
||||
...vcsSegmentEventProperties('git', 'pull', errorMessage),
|
||||
trackAnalyticsEvent(AnalyticsEvent.vcsAction, {
|
||||
...vcsEventProperties('git', 'pull', errorMessage),
|
||||
providerName,
|
||||
repoId: gitRepository._id,
|
||||
});
|
||||
@@ -2679,7 +2679,7 @@ const getRepositoryDirectoryTree = async ({
|
||||
repositoryTree: FileTree;
|
||||
folderList: Record<string, string[]>;
|
||||
}> => {
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
|
||||
if (project && models.project.isEmptyGitProject(project)) {
|
||||
return {
|
||||
@@ -2788,9 +2788,9 @@ async function completeSignInToGitProvider({
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
trackSegmentEvent(SegmentEvent.gitAuthenticationUpdated, { provider });
|
||||
trackAnalyticsEvent(AnalyticsEvent.gitAuthenticationUpdated, { provider });
|
||||
} else {
|
||||
trackSegmentEvent(SegmentEvent.gitAuthenticationCompleted, { provider });
|
||||
trackAnalyticsEvent(AnalyticsEvent.gitAuthenticationCompleted, { provider });
|
||||
}
|
||||
|
||||
return {};
|
||||
@@ -2893,7 +2893,7 @@ export async function runAllGitRepoMigrations(): Promise<MigrationSummary> {
|
||||
const logs: string[] = [];
|
||||
const failedProjects: { id: string; name: string }[] = [];
|
||||
|
||||
const allProjects = await services.project.all();
|
||||
const allProjects = await services.project.list();
|
||||
const gitProjects = allProjects.filter((p): p is GitProject => models.project.isConnectedGitProject(p));
|
||||
|
||||
if (gitProjects.length === 0) return { logs, failedProjects, totalProjects: 0 };
|
||||
@@ -2947,7 +2947,7 @@ export async function runAllGitRepoMigrations(): Promise<MigrationSummary> {
|
||||
failedProjects.map(async ({ id, name }) => {
|
||||
logs.push(`${ts()} [INFO] ["${name}"] Converting to local project`);
|
||||
try {
|
||||
const project = await services.project.getById(id);
|
||||
const project = await services.project.get(id);
|
||||
if (!project || !models.project.isConnectedGitProject(project)) {
|
||||
logs.push(`${ts()} [WARN] ["${name}"] Project not found or already local — skipping`);
|
||||
return;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { promisify } from 'node:util';
|
||||
import { app, net } from 'electron';
|
||||
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent, trackSegmentEvent } from '~/main/analytics';
|
||||
import { AnalyticsEvent, trackAnalyticsEvent } from '~/main/analytics';
|
||||
|
||||
import { isDevelopment } from '../common/constants';
|
||||
import { validatePluginName } from '../utils/plugin';
|
||||
@@ -160,7 +160,7 @@ export default async function installPlugin(pluginName: string, allowScopedPacka
|
||||
}),
|
||||
);
|
||||
|
||||
trackSegmentEvent(SegmentEvent.installPlugin, {
|
||||
trackAnalyticsEvent(AnalyticsEvent.installPlugin, {
|
||||
pluginName: moduleName,
|
||||
pluginVersion: info.version,
|
||||
});
|
||||
|
||||
@@ -192,7 +192,7 @@ export type MainOnChannels =
|
||||
| 'socketIO.event.on'
|
||||
| 'startExecution'
|
||||
| 'trackPageView'
|
||||
| 'trackSegmentEvent'
|
||||
| 'trackAnalyticsEvent'
|
||||
| 'updateLatestStepName'
|
||||
| 'webSocket.close'
|
||||
| 'webSocket.closeAll'
|
||||
|
||||
@@ -37,8 +37,8 @@ import type {
|
||||
|
||||
import type { HiddenBrowserWindowBridgeAPI } from '../../entry.hidden-window';
|
||||
import type { PluginTemplateTag, RenderedRequest } from '../../templating/types';
|
||||
import type { SegmentEvent } from '../analytics';
|
||||
import { setCurrentOrganizationId, trackPageView, trackSegmentEvent } from '../analytics';
|
||||
import type { AnalyticsEvent } from '../analytics';
|
||||
import { setCurrentOrganizationId, trackAnalyticsEvent, trackPageView } from '../analytics';
|
||||
import {
|
||||
authorizeUserInDefaultBrowser,
|
||||
cancelAuthorizationInDefaultBrowser,
|
||||
@@ -186,7 +186,7 @@ export interface RendererToMainBridgeAPI {
|
||||
secretStorage: secretStorageBridgeAPI;
|
||||
electronStorage: electronStorageBridgeAPI;
|
||||
sync: SyncBridgeAPI;
|
||||
trackSegmentEvent: (options: { event: string; properties?: Record<string, unknown> }) => void;
|
||||
trackAnalyticsEvent: (options: { event: string; properties?: Record<string, unknown> }) => void;
|
||||
trackPageView: (options: { name: string }) => void;
|
||||
setCurrentOrganizationId: (organizationId: string | undefined) => void;
|
||||
showNunjucksContextMenu: (options: {
|
||||
@@ -399,10 +399,13 @@ export function registerMainHandlers() {
|
||||
ipcMainHandle('readDir', readDir);
|
||||
|
||||
ipcMainHandle('readOrCreateDataDir', async (_, options: { folder: string }) => {
|
||||
const dataPath = app.getPath('userData');
|
||||
const folderPath = path.join(dataPath, options.folder);
|
||||
const folderPath = path.join(app.getPath('userData'), options.folder);
|
||||
mkdirSync(folderPath, { recursive: true });
|
||||
return readDir(_, { path: folderPath });
|
||||
try {
|
||||
return await readDir(_, { path: folderPath });
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
ipcMainHandle('curlRequest', (_, options: Parameters<typeof curlRequest>[0]) => {
|
||||
@@ -413,9 +416,12 @@ export function registerMainHandlers() {
|
||||
cancelCurlRequest(requestId);
|
||||
});
|
||||
|
||||
ipcMainOn('trackSegmentEvent', (_, options: { event: SegmentEvent; properties?: Record<string, unknown> }): void => {
|
||||
trackSegmentEvent(options.event, options.properties);
|
||||
});
|
||||
ipcMainOn(
|
||||
'trackAnalyticsEvent',
|
||||
(_, options: { event: AnalyticsEvent; properties?: Record<string, unknown> }): void => {
|
||||
trackAnalyticsEvent(options.event, options.properties);
|
||||
},
|
||||
);
|
||||
ipcMainOn('trackPageView', (_, options: { name: string }): void => {
|
||||
trackPageView(options.name);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { app } from 'electron';
|
||||
|
||||
import { LLM_BACKENDS } from '~/common/constants';
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent, trackSegmentEvent } from '~/main/analytics';
|
||||
import { AnalyticsEvent, trackAnalyticsEvent } from '~/main/analytics';
|
||||
import { ipcMainHandle } from '~/main/ipc/electron';
|
||||
|
||||
const LLM_PLUGIN_NAME = 'insomnia-llm';
|
||||
@@ -123,7 +123,7 @@ export const getAIFeatureEnabled = async (feature: AIFeatureNames): Promise<bool
|
||||
export const setAIFeatureEnabled = async (feature: AIFeatureNames, enabled: boolean): Promise<void> => {
|
||||
await services.pluginData.upsertByKey(LLM_PLUGIN_NAME, `feature.${feature}`, String(enabled));
|
||||
|
||||
trackSegmentEvent(enabled ? SegmentEvent.aiFeatureEnabled : SegmentEvent.aiFeatureDisabled, {
|
||||
trackAnalyticsEvent(enabled ? AnalyticsEvent.aiFeatureEnabled : AnalyticsEvent.aiFeatureDisabled, {
|
||||
feature: feature,
|
||||
set_for: 'user',
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
import { METHOD_SUBSCRIBE_RESOURCE, METHOD_UNSUBSCRIBE_RESOURCE } from '~/common/mcp-utils';
|
||||
import { SegmentEvent, trackSegmentEvent } from '~/main/analytics';
|
||||
import { AnalyticsEvent, trackAnalyticsEvent } from '~/main/analytics';
|
||||
import { getActiveMcpClient, getReadyActiveMcpConnectionContext, writeEventLogAndNotify } from '~/main/mcp/common';
|
||||
import type { CommonMcpOptions, McpMessageEventWithoutBase } from '~/main/mcp/types';
|
||||
|
||||
@@ -29,7 +29,7 @@ export const callTool = async (options: CommonMcpOptions & CallToolRequest['para
|
||||
const mcpClient = getActiveMcpClient(requestId);
|
||||
if (mcpClient) {
|
||||
const response = await mcpClient.callTool(params, CompatibilityCallToolResultSchema);
|
||||
trackSegmentEvent(SegmentEvent.mcpToolCalled);
|
||||
trackAnalyticsEvent(AnalyticsEvent.mcpToolCalled);
|
||||
return response.content;
|
||||
}
|
||||
return null;
|
||||
@@ -49,7 +49,7 @@ export const getPrompt = async (options: CommonMcpOptions & GetPromptRequest['pa
|
||||
const mcpClient = getActiveMcpClient(options.requestId);
|
||||
if (mcpClient) {
|
||||
const prompt = await mcpClient.getPrompt(params);
|
||||
trackSegmentEvent(SegmentEvent.mcpPromptCalled);
|
||||
trackAnalyticsEvent(AnalyticsEvent.mcpPromptCalled);
|
||||
return prompt;
|
||||
}
|
||||
return null;
|
||||
@@ -80,7 +80,7 @@ export const readResource = async (options: CommonMcpOptions & ReadResourceReque
|
||||
const mcpClient = getActiveMcpClient(requestId);
|
||||
if (mcpClient) {
|
||||
const resource = await mcpClient.readResource(params);
|
||||
trackSegmentEvent(SegmentEvent.mcpResourceRead);
|
||||
trackAnalyticsEvent(AnalyticsEvent.mcpResourceRead);
|
||||
return resource;
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -21,7 +21,7 @@ import electron from 'electron';
|
||||
import { getAppVersion, getProductName, REALTIME_EVENTS_CHANNELS } from '~/common/constants';
|
||||
import { getMcpMethodFromMessage, METHOD_NOTIFICATION_CANCELLED } from '~/common/mcp-utils';
|
||||
import { models, services } from '~/insomnia-data';
|
||||
import { SegmentEvent, trackSegmentEvent } from '~/main/analytics';
|
||||
import { AnalyticsEvent, trackAnalyticsEvent } from '~/main/analytics';
|
||||
import {
|
||||
callTool,
|
||||
getPrompt,
|
||||
@@ -257,7 +257,7 @@ const createTransportAndConnect = async (context: ConnectionContext, mcpClient:
|
||||
}
|
||||
const authDisabled = 'disabled' in mcpRequest.authentication && mcpRequest.authentication.disabled;
|
||||
const isFirstConnection = !mcpRequest.connected;
|
||||
trackSegmentEvent(SegmentEvent.mcpClientConnected, {
|
||||
trackAnalyticsEvent(AnalyticsEvent.mcpClientConnected, {
|
||||
transportType: connectionOptions.transportType,
|
||||
firstTime: isFirstConnection,
|
||||
...(connectionOptions.transportType === models.mcpRequest.TRANSPORT_TYPES.HTTP
|
||||
@@ -455,7 +455,7 @@ const closeMcpConnection = async (options: CommonMcpOptions) => {
|
||||
// Execute clear resource subscription in main process rather than UI to make sure closeAllMcpConnections method will clear subscriptions
|
||||
await services.mcpRequest.clearResourceSubscriptions(requestId);
|
||||
}
|
||||
trackSegmentEvent(SegmentEvent.mcpClientDisconnected);
|
||||
trackAnalyticsEvent(AnalyticsEvent.mcpClientDisconnected);
|
||||
};
|
||||
|
||||
const closeAllMcpConnections = () => {
|
||||
|
||||
@@ -35,10 +35,6 @@ describe('getAuthQueryParams', () => {
|
||||
});
|
||||
});
|
||||
describe('sendCurlAndWriteTimeline()', () => {
|
||||
beforeEach(async () => {
|
||||
await services.project.all();
|
||||
});
|
||||
|
||||
it('sends a generic request', async () => {
|
||||
const workspace = await services.workspace.create();
|
||||
const settings = await services.settings.getOrCreate();
|
||||
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
GIT_PROVIDER_COMPLETE_SIGN_IN_FETCHER_KEY,
|
||||
useGitProviderCompleteSignInFetcher,
|
||||
} from '~/routes/git-credentials.complete-sign-in';
|
||||
import { PENDING_IMPORT_ATTRIBUTION_KEY, SegmentEvent, trackImportEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent, PENDING_IMPORT_ATTRIBUTION_KEY, trackImportEvent } from '~/ui/analytics';
|
||||
import { getLoginUrl } from '~/ui/auth-session-provider.client';
|
||||
import { CopyButton } from '~/ui/components/base/copy-button';
|
||||
import { Link } from '~/ui/components/base/link';
|
||||
@@ -161,7 +161,7 @@ export const useRootLoaderData = () => {
|
||||
export async function clientLoader(_args: Route.ClientLoaderArgs) {
|
||||
const settings = await services.settings.get();
|
||||
const workspaceCount = await services.workspace.count();
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
const cloudCredentials = await services.cloudCredential.all();
|
||||
|
||||
return {
|
||||
@@ -203,6 +203,11 @@ export const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
*
|
||||
insomnia://*
|
||||
;
|
||||
frame-src
|
||||
blob:
|
||||
*
|
||||
insomnia://*
|
||||
;
|
||||
script-src
|
||||
'self'
|
||||
'unsafe-eval'
|
||||
@@ -381,15 +386,14 @@ const Root = () => {
|
||||
JSON.stringify({ importSource, importSourceUrl }),
|
||||
);
|
||||
}
|
||||
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
if (!userSession.id) {
|
||||
window.sessionStorage.setItem('pendingDeepLinkAfterAuthorize', url);
|
||||
window.localStorage.setItem('logoutMessage', 'Please log in to import this resource.');
|
||||
trackImportEvent(SegmentEvent.importLoginRequired);
|
||||
trackImportEvent(AnalyticsEvent.importLoginRequired);
|
||||
return navigate(href('/auth/login'));
|
||||
}
|
||||
trackImportEvent(SegmentEvent.importStarted, { source: 'import-url' });
|
||||
trackImportEvent(AnalyticsEvent.importStarted, { source: 'import-url' });
|
||||
|
||||
if (params.uri) {
|
||||
return setImportObject({
|
||||
@@ -511,7 +515,7 @@ const Root = () => {
|
||||
if (urlWithoutParams === 'insomnia://app/open/organization') {
|
||||
// if user is logged out, navigate to authorize instead
|
||||
// gracefully handle open org in app from browser
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
if (!userSession.id || userSession.id === '') {
|
||||
const url = new URL(getLoginUrl());
|
||||
window.main.openInBrowser(url.toString());
|
||||
@@ -635,7 +639,7 @@ const Root = () => {
|
||||
if (pendingDeepLink && organizationId && organizationId !== models.organization.SCRATCHPAD_ORGANIZATION_ID) {
|
||||
window.sessionStorage.removeItem('pendingDeepLinkAfterAuthorize');
|
||||
window.sessionStorage.setItem('suppressWelcomeModals', 'true');
|
||||
trackImportEvent(SegmentEvent.importResumedAfterLogin);
|
||||
trackImportEvent(AnalyticsEvent.importResumedAfterLogin);
|
||||
window.main.openDeepLink(pendingDeepLink);
|
||||
}
|
||||
}, [organizationId]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { href } from 'react-router';
|
||||
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { showToast } from '~/ui/components/toast-notification';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -38,8 +38,8 @@ export async function clientAction(args: Route.ClientActionArgs) {
|
||||
.join('\n'),
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.recommendCommitsGenerated,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.recommendCommitsGenerated,
|
||||
properties: {
|
||||
file_count: commits?.map(commit => commit.files?.length || 0)?.reduce((a, b) => a + b, 0),
|
||||
group_count: commits?.length || 0,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Button, Heading } from 'react-aria-components';
|
||||
import { href, redirect, useFetchers, useNavigate } from 'react-router';
|
||||
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { getLoginUrl, submitAuthCode } from '~/ui/auth-session-provider.client';
|
||||
import { Icon } from '~/ui/components/icon';
|
||||
import { validateVaultKey } from '~/ui/vault-key.client';
|
||||
@@ -32,18 +32,18 @@ export async function clientAction({ request }: Route.ClientActionArgs) {
|
||||
};
|
||||
}
|
||||
console.log('Login successful');
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.loginSuccess,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.loginSuccess,
|
||||
});
|
||||
window.localStorage.setItem('hasUserLoggedInBefore', 'true');
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
const { accountId, id: sessionId } = userSession;
|
||||
try {
|
||||
// check vault salt exists in server
|
||||
const { salt: vaultSalt } = await getVault({ sessionId });
|
||||
if (vaultSalt) {
|
||||
// save vault salt to session
|
||||
await services.userSession.update(userSession, { vaultSalt });
|
||||
await services.userSession.update({ vaultSalt });
|
||||
// get vault key saved in local
|
||||
const localVaultKey = await getVaultKeyFromStorage(accountId);
|
||||
if (localVaultKey) {
|
||||
@@ -52,7 +52,7 @@ export async function clientAction({ request }: Route.ClientActionArgs) {
|
||||
if (validateResult) {
|
||||
// Encrypt vault key and save encrypted vault key & raw vault salt to session
|
||||
const encryptedVaultKey = await window.main.secretStorage.encryptString(localVaultKey);
|
||||
await services.userSession.update(userSession, { vaultKey: encryptedVaultKey, vaultSalt });
|
||||
await services.userSession.update({ vaultKey: encryptedVaultKey, vaultSalt });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Route } from './+types/auth.clear-vault-key';
|
||||
export async function clientAction({ request }: Route.ClientActionArgs) {
|
||||
const { organizations = [], sessionId: resetVaultClientSessionId } = await request.json();
|
||||
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
const { id: sessionId } = userSession;
|
||||
const { salt: newVaultSalt } =
|
||||
(await getVault({ sessionId }).catch(error => {
|
||||
@@ -21,7 +21,7 @@ export async function clientAction({ request }: Route.ClientActionArgs) {
|
||||
// remove all secret environment variables
|
||||
await services.environment.removeAllSecrets(organizations);
|
||||
// Update vault salt and delete vault key from session
|
||||
await services.userSession.update(userSession, { vaultSalt: newVaultSalt, vaultKey: '' });
|
||||
await services.userSession.update({ vaultSalt: newVaultSalt, vaultKey: '' });
|
||||
// show notification
|
||||
electron.ipcRenderer.emit('show-toast', null, {
|
||||
content: {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Button } from 'react-aria-components';
|
||||
import { href, redirect, useNavigate } from 'react-router';
|
||||
|
||||
import { models } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { getLoginUrl } from '~/ui/auth-session-provider.client';
|
||||
import { Icon } from '~/ui/components/icon';
|
||||
import { Tooltip } from '~/ui/components/tooltip';
|
||||
@@ -152,8 +152,8 @@ const Component = () => {
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.selectScratchpad,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.selectScratchpad,
|
||||
});
|
||||
navigate(
|
||||
href('/organization/:organizationId/project/:projectId/workspace/:workspaceId/debug', {
|
||||
|
||||
@@ -6,11 +6,11 @@ import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
export async function clientAction(_args: ActionFunctionArgs) {
|
||||
try {
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
const { id: sessionId } = userSession;
|
||||
const { salt: vaultSalt } = await getVault({ sessionId });
|
||||
if (vaultSalt) {
|
||||
await services.userSession.update(userSession, { vaultSalt });
|
||||
await services.userSession.update({ vaultSalt });
|
||||
return vaultSalt;
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
export async function clientAction({ request }: ActionFunctionArgs) {
|
||||
const { vaultKey, saveVaultKey: saveVaultKeyLocally = false } = await request.json();
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
const { vaultSalt, accountId } = userSession;
|
||||
|
||||
if (!vaultSalt) {
|
||||
|
||||
@@ -38,7 +38,7 @@ export async function clientLoader(args: Route.ClientLoaderArgs) {
|
||||
);
|
||||
};
|
||||
|
||||
const { accountId } = await services.userSession.getOrCreate();
|
||||
const { accountId } = await services.userSession.get();
|
||||
|
||||
const allOrganizations = JSON.parse(localStorage.getItem(`${accountId}:organizations`) || '[]') as Organization[];
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
|
||||
const gitRepositoryIds = relatedGitRepositories.map(repo => repo._id);
|
||||
|
||||
const relatedProjects = await services.project.getAllByGitRepositoryIds(gitRepositoryIds);
|
||||
const relatedProjects = await services.project.list({ gitRepositoryIds });
|
||||
|
||||
return {
|
||||
projects: relatedProjects,
|
||||
|
||||
@@ -90,7 +90,7 @@ const MigrationView = () => {
|
||||
<p className="text-sm">We hit an unexpected error while updating your file system. Please try again.</p>
|
||||
<p className="text-sm text-[#828282]">
|
||||
Having trouble and need to contact us, or back up to an old version? See our{' '}
|
||||
<ExternalLink className="underline" href="https://developer.konghq.com/insomnia/git-sync/">
|
||||
<ExternalLink className="underline" href="https://developer.konghq.com/insomnia/upgrade/insomnia-12.6/">
|
||||
docs.
|
||||
</ExternalLink>
|
||||
</p>
|
||||
@@ -103,7 +103,7 @@ const MigrationView = () => {
|
||||
</p>
|
||||
<p className="text-sm">
|
||||
Note: This change is backwards compatible, but we strongly recommend{' '}
|
||||
<ExternalLink className="underline" href="https://developer.konghq.com/insomnia/git-sync/">
|
||||
<ExternalLink className="underline" href="https://developer.konghq.com/insomnia/upgrade/insomnia-12.6/">
|
||||
following these best practices
|
||||
</ExternalLink>{' '}
|
||||
when returning to an earlier version of Insomnia.
|
||||
|
||||
@@ -7,7 +7,7 @@ import { models, services } from '~/insomnia-data';
|
||||
import { createFetcherLoadHook } from '~/utils/router';
|
||||
|
||||
export async function clientLoader() {
|
||||
const { accountId } = await services.userSession.getOrCreate();
|
||||
const { accountId } = await services.userSession.get();
|
||||
const organizations = JSON.parse(localStorage.getItem(`${accountId}:organizations`) || '[]') as Organization[];
|
||||
const allProjects = (
|
||||
await Promise.all(
|
||||
|
||||
@@ -35,7 +35,7 @@ export const importScannedResources = async ({
|
||||
invariant(organizationId && typeof organizationId === 'string', 'OrganizationId is required.');
|
||||
invariant(projectId && typeof projectId === 'string', 'ProjectId is required.');
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found.');
|
||||
|
||||
return await (typeof workspaceId === 'string' && workspaceId
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
scanResources,
|
||||
} from '~/common/import';
|
||||
import type { ImportEntry } from '~/main/importers/entities';
|
||||
import { SegmentEvent, trackImportEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent, trackImportEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -27,7 +27,7 @@ export const scanImportResources = async (data: {
|
||||
invariant(typeof source === 'string', 'Source is required.');
|
||||
invariant(IMPORT_SOURCE_TYPES.includes(source), 'Unsupported import type');
|
||||
|
||||
trackImportEvent(SegmentEvent.importScanned, { source });
|
||||
trackImportEvent(AnalyticsEvent.importScanned, { source });
|
||||
|
||||
const contentList: ImportEntry[] = [];
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
const { organizationId, invitationId } = params;
|
||||
|
||||
try {
|
||||
const user = await services.userSession.getOrCreate();
|
||||
const user = await services.userSession.get();
|
||||
const sessionId = user.id;
|
||||
|
||||
const response = await reinvite({
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
invariant(typeof roleId === 'string', 'Role ID is required');
|
||||
|
||||
try {
|
||||
const user = await services.userSession.getOrCreate();
|
||||
const user = await services.userSession.get();
|
||||
const sessionId = user.id;
|
||||
|
||||
const response = await updateInvitationRole({
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
invariant(typeof roleId === 'string', 'Role ID is required');
|
||||
|
||||
try {
|
||||
const user = await services.userSession.getOrCreate();
|
||||
const user = await services.userSession.get();
|
||||
const sessionId = user.id;
|
||||
const response = await updateUserRoles({
|
||||
organizationId,
|
||||
|
||||
@@ -26,7 +26,7 @@ export const fallbackBilling = Object.freeze<Billing>({
|
||||
|
||||
export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
const { organizationId } = params;
|
||||
const { id: sessionId, accountId } = await services.userSession.getOrCreate();
|
||||
const { id: sessionId, accountId } = await services.userSession.get();
|
||||
|
||||
if (models.organization.isScratchpadOrganizationId(organizationId)) {
|
||||
return {
|
||||
|
||||
@@ -37,7 +37,7 @@ import { useInsomniaSyncPullRemoteFileActionFetcher } from '~/routes/organizatio
|
||||
import { useProjectLoaderData } from '~/routes/organization.$organizationId.project.$projectId';
|
||||
import { useWorkspaceNewActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.new';
|
||||
import { useStorageRulesLoaderFetcher } from '~/routes/organization.$organizationId.storage-rules';
|
||||
import { SegmentEvent, trackOnceDaily } from '~/ui/analytics';
|
||||
import { AnalyticsEvent, trackOnceDaily } from '~/ui/analytics';
|
||||
import { AvatarGroup } from '~/ui/components/avatar';
|
||||
import { WorkspaceCardDropdown } from '~/ui/components/dropdowns/workspace-card-dropdown';
|
||||
import { ErrorBoundary } from '~/ui/components/error-boundary';
|
||||
@@ -389,7 +389,7 @@ const Component = () => {
|
||||
onChange={filter => {
|
||||
setWorkspaceListFilter(filter);
|
||||
if (filter.trim() !== '') {
|
||||
trackOnceDaily(SegmentEvent.homepageFiltered);
|
||||
trackOnceDaily(AnalyticsEvent.homepageFiltered);
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -483,8 +483,8 @@ const Component = () => {
|
||||
|
||||
<Button
|
||||
onPress={() => {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.importStarted,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.importStarted,
|
||||
properties: {
|
||||
source: 'project',
|
||||
},
|
||||
|
||||
@@ -14,10 +14,10 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
const { organizationId, projectId } = params;
|
||||
invariant(organizationId, 'Organization ID is required');
|
||||
invariant(projectId, 'Project ID is required');
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
const user = await services.userSession.getOrCreate();
|
||||
const user = await services.userSession.get();
|
||||
const sessionId = user.id;
|
||||
invariant(sessionId, 'User must be logged in to delete a project');
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ async function getAllLocalFiles({ projectId }: { projectId: string }) {
|
||||
export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
const { organizationId, projectId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, `Project was not found ${projectId}`);
|
||||
const organizationProjects =
|
||||
(await database.find<Project>(models.project.type, {
|
||||
|
||||
@@ -11,7 +11,7 @@ export async function clientAction({ request }: Route.ClientActionArgs) {
|
||||
const projectId = formData.get('projectId');
|
||||
const workspaceId = formData.get('workspaceId');
|
||||
invariant(typeof projectId === 'string', 'Project ID is required');
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
invariant(typeof workspaceId === 'string', 'Workspace ID is required');
|
||||
|
||||
@@ -14,7 +14,7 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
|
||||
invariant(typeof organizationId === 'string', 'Organization ID is required');
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
await services.project.update(project, {
|
||||
|
||||
@@ -61,7 +61,7 @@ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
invariant(organizationId, 'Organization ID is required');
|
||||
|
||||
if (!models.project.isScratchpadProject({ _id: projectId })) {
|
||||
const { id: sessionId } = await services.userSession.getOrCreate();
|
||||
const { id: sessionId } = await services.userSession.get();
|
||||
|
||||
if (!sessionId) {
|
||||
await logout();
|
||||
@@ -69,7 +69,7 @@ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
}
|
||||
}
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
|
||||
if (!project) {
|
||||
return redirect(href('/organization/:organizationId', { organizationId }));
|
||||
|
||||
@@ -6,7 +6,7 @@ import { projectLock } from '~/common/project';
|
||||
import type { WorkspaceMeta } from '~/insomnia-data';
|
||||
import { models, services } from '~/insomnia-data';
|
||||
import { reportGitProjectCount } from '~/routes/organization.$organizationId.project.new';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { showToast } from '~/ui/components/toast-notification';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
@@ -36,13 +36,13 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
|
||||
const { organizationId, projectId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
const effectiveRepoId = models.project.isGitProject(project) ? models.project.getEffectiveRepoId(project) : null;
|
||||
const gitRepository = effectiveRepoId ? await services.gitRepository.getById(effectiveRepoId) : null;
|
||||
|
||||
const user = await services.userSession.getOrCreate();
|
||||
const user = await services.userSession.get();
|
||||
const sessionId = user.id;
|
||||
|
||||
try {
|
||||
@@ -106,8 +106,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
sessionId,
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.projectUpdated,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.projectUpdated,
|
||||
properties: {
|
||||
storage: 'local',
|
||||
},
|
||||
@@ -158,8 +158,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
name,
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.projectUpdated,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.projectUpdated,
|
||||
properties: {
|
||||
storage: 'remote',
|
||||
},
|
||||
@@ -222,8 +222,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
sessionId,
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.projectUpdated,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.projectUpdated,
|
||||
properties: {
|
||||
storage: 'git',
|
||||
},
|
||||
@@ -376,8 +376,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
// local project rename
|
||||
await services.project.update(project, { name });
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.projectUpdated,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.projectUpdated,
|
||||
properties: {
|
||||
storage: 'local',
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function clientAction({ params, request }: Route.ClientActionArgs)
|
||||
return;
|
||||
}
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found for request');
|
||||
if (accessLevel === 'project') {
|
||||
await services.project.update(project, { mcpStdioAccess: true });
|
||||
|
||||
@@ -6,12 +6,14 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { getContentDispositionHeader } from '~/common/misc';
|
||||
import type {
|
||||
Environment,
|
||||
Request,
|
||||
RequestGroup,
|
||||
RequestMeta,
|
||||
ResponseInfo,
|
||||
RunnerResultPerRequestPerIteration,
|
||||
UserUploadEnvironment,
|
||||
} from '~/insomnia-data';
|
||||
import { models, services } from '~/insomnia-data';
|
||||
import { database as db, models, services } from '~/insomnia-data';
|
||||
import type { ResponsePatch } from '~/main/network/libcurl-promise';
|
||||
import type { TimingStep } from '~/main/network/request-timing';
|
||||
import {
|
||||
@@ -25,7 +27,7 @@ import {
|
||||
tryToInterpolateRequest,
|
||||
tryToTransformRequestWithPlugins,
|
||||
} from '~/network/network';
|
||||
import { type ImportAttribution, importAttributionKey, SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent, type ImportAttribution, importAttributionKey } from '~/ui/analytics';
|
||||
import { parseGraphQLReqeustBody } from '~/utils/graph-ql';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
@@ -356,8 +358,22 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
const activeRequest = await services.request.getById(requestId);
|
||||
|
||||
if (activeRequest) {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.requestExecuted,
|
||||
const [requestAndAncestors, clientCertificates] = await Promise.all([
|
||||
db.withAncestors<Request | RequestGroup>(activeRequest as Request, [
|
||||
models.request.type,
|
||||
models.requestGroup.type,
|
||||
]),
|
||||
services.clientCertificate.findByParentId(workspaceId),
|
||||
]);
|
||||
const docsWithScripts = requestAndAncestors.filter(
|
||||
(doc): doc is Request | RequestGroup =>
|
||||
models.request.isRequest(doc) || models.requestGroup.isRequestGroup(doc),
|
||||
);
|
||||
const allPreScripts = docsWithScripts.map(doc => doc.preRequestScript).filter((s): s is string => !!s);
|
||||
const allPostScripts = docsWithScripts.map(doc => doc.afterResponseScript).filter((s): s is string => !!s);
|
||||
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.requestExecuted,
|
||||
properties: {
|
||||
project_id: projectId,
|
||||
collection_id: workspaceId,
|
||||
@@ -371,6 +387,14 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
count_headers: response.headers.length,
|
||||
count_cookies: response.headers.find(h => h.name === 'set-cookie')?.value.split(',').length || 0,
|
||||
count_tests: response.requestTestResults?.length || 0,
|
||||
has_prescript: allPreScripts.length > 0,
|
||||
has_postscript: allPostScripts.length > 0,
|
||||
count_prescript_lines: allPreScripts.reduce((sum, s) => sum + s.split('\n').length, 0),
|
||||
count_postscript_lines: allPostScripts.reduce((sum, s) => sum + s.split('\n').length, 0),
|
||||
count_query_parameters: activeRequest.parameters?.length ?? 0,
|
||||
count_path_parameters: activeRequest.pathParameters?.length ?? 0,
|
||||
has_docs: !!activeRequest.description,
|
||||
count_certificates: clientCertificates.length,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -379,8 +403,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
if (jsonImportAttribution) {
|
||||
try {
|
||||
const importAttribution = JSON.parse(jsonImportAttribution) as ImportAttribution;
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.importedRequestFirstSend,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.importedRequestFirstSend,
|
||||
properties: {
|
||||
...importAttribution,
|
||||
protocol: activeRequest.type,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { href } from 'react-router';
|
||||
|
||||
import type { WebSocketRequest } from '~/insomnia-data';
|
||||
import { models, services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { updateMimeType } from '~/ui/components/dropdowns/content-type-dropdown';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
@@ -45,8 +45,8 @@ export async function clientAction({ params, request }: Route.ClientActionArgs)
|
||||
await services.helpers.updateRequest(req, patch);
|
||||
|
||||
if (req.name !== patch.name) {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.requestRenamed,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.requestRenamed,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { href, redirect } from 'react-router';
|
||||
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -19,8 +19,8 @@ export async function clientAction({ params, request }: Route.ClientActionArgs)
|
||||
const workspaceMeta = await services.workspaceMeta.getByParentId(workspaceId);
|
||||
invariant(workspaceMeta, 'Workspace meta not found');
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.requestDeleted,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.requestDeleted,
|
||||
});
|
||||
|
||||
if (workspaceMeta.activeRequestId === id) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '~/common/constants';
|
||||
import type { Request, RequestBody, RequestParameter } from '~/insomnia-data';
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import type { CreateRequestType } from '~/ui/hooks/use-request';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
@@ -126,8 +126,8 @@ export async function clientAction({ params, request }: Route.ClientActionArgs)
|
||||
|
||||
const certificates = await services.clientCertificate.findByParentId(workspaceId);
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.requestCreated,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.requestCreated,
|
||||
properties: {
|
||||
requestType,
|
||||
protocol: requestType,
|
||||
|
||||
@@ -32,7 +32,7 @@ import { useRootLoaderData } from '~/root';
|
||||
import { useOrganizationLoaderData } from '~/routes/organization';
|
||||
import type { CollectionRunnerContext } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.send';
|
||||
import { sendActionImplementation } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.$requestId.send';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { Dropdown, DropdownItem, ItemContent } from '~/ui/components/base/dropdown';
|
||||
import { ErrorBoundary } from '~/ui/components/error-boundary';
|
||||
import { HelpTooltip } from '~/ui/components/help-tooltip';
|
||||
@@ -270,8 +270,8 @@ export const Runner: FC = () => {
|
||||
}
|
||||
setIsRunning(true);
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.collectionRunExecute,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.collectionRunExecute,
|
||||
properties: { plan: organizationData?.currentPlan?.type || 'scratchpad', iterations: iterationCount },
|
||||
});
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ import { useRequestNewActionFetcher } from '~/routes/organization.$organizationI
|
||||
import { useRequestGroupNewActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request-group.new';
|
||||
import Runner from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.runner';
|
||||
import { useToggleExpandAllActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.toggle-expand-all';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { DropdownHint } from '~/ui/components/base/dropdown/dropdown-hint';
|
||||
import { DocumentTab } from '~/ui/components/document-tab';
|
||||
import { RequestActionsDropdown } from '~/ui/components/dropdowns/request-actions-dropdown';
|
||||
@@ -138,7 +138,7 @@ export async function clientLoader({ params, request }: Route.ClientLoaderArgs)
|
||||
if (!params.requestId && !params.requestGroupId) {
|
||||
const { projectId, workspaceId, organizationId } = params;
|
||||
|
||||
const activeProject = await services.project.getById(projectId);
|
||||
const activeProject = await services.project.get(projectId);
|
||||
if (!activeProject) {
|
||||
showResourceNotFoundToast(`Project not found: ${projectId}`);
|
||||
throw redirect(href('/organization/:organizationId/project', { organizationId }));
|
||||
@@ -807,8 +807,8 @@ const Debug = () => {
|
||||
setFilter(value);
|
||||
|
||||
if (value.trim() !== '') {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.filterCreatedRequests,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.filterCreatedRequests,
|
||||
});
|
||||
}
|
||||
}}
|
||||
@@ -829,8 +829,8 @@ const Debug = () => {
|
||||
selectedKey={sortOrder}
|
||||
onSelectionChange={order => {
|
||||
if (order) {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.requestListSortClicked,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.requestListSortClicked,
|
||||
});
|
||||
setSearchParams({
|
||||
...Object.fromEntries(searchParams.entries()),
|
||||
@@ -884,8 +884,8 @@ const Debug = () => {
|
||||
defaultSelected={allExpanded}
|
||||
onChange={() => {
|
||||
setAllExpanded(!allExpanded);
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.requestListExpandCollapseClicked,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.requestListExpandCollapseClicked,
|
||||
});
|
||||
toggleExpandAllFetcher.submit({
|
||||
organizationId,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { href } from 'react-router';
|
||||
|
||||
import { EnvironmentType, services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -22,8 +22,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
isPrivate,
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.environmentCreate,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.environmentCreate,
|
||||
properties: { type: isPrivate ? 'private' : 'global' },
|
||||
});
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
try {
|
||||
await window.main.sync.takeSnapshot(data.message);
|
||||
if (data.push) {
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
invariant(project.remoteId, 'Project is not remote');
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Route } from './+types/organization.$organizationId.project.$proje
|
||||
export async function clientAction({ request, params }: Route.ClientActionArgs) {
|
||||
const { projectId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
const formData = await request.formData();
|
||||
|
||||
@@ -2,8 +2,8 @@ import { href } from 'react-router';
|
||||
|
||||
import { database } from '~/common/database';
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { getSyncItems, remoteCompareCache, vcsSegmentEventProperties } from '~/ui/sync-utils';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { getSyncItems, remoteCompareCache, vcsEventProperties } from '~/ui/sync-utils';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { Route } from './+types/organization.$organizationId.project.$proje
|
||||
export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
const { projectId, workspaceId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
const { syncItems } = await getSyncItems({ workspaceId });
|
||||
try {
|
||||
@@ -24,9 +24,9 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
projectId: project._id,
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.vcsAction,
|
||||
properties: vcsSegmentEventProperties('remote', 'pull'),
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.vcsAction,
|
||||
properties: vcsEventProperties('remote', 'pull'),
|
||||
});
|
||||
// This is to synchronize the local database with the branch changes
|
||||
await database.batchModifyDocs(delta);
|
||||
@@ -38,9 +38,9 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error while pulling from remote.';
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.vcsAction,
|
||||
properties: vcsSegmentEventProperties('remote', 'pull', errorMessage),
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.vcsAction,
|
||||
properties: vcsEventProperties('remote', 'pull', errorMessage),
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { href } from 'react-router';
|
||||
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { remoteCompareCache, vcsSegmentEventProperties } from '~/ui/sync-utils';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { remoteCompareCache, vcsEventProperties } from '~/ui/sync-utils';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { Route } from './+types/organization.$organizationId.project.$proje
|
||||
export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
const { projectId, workspaceId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
invariant(project.remoteId, 'Project is not remote');
|
||||
|
||||
@@ -21,9 +21,9 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
teamProjectId: project.remoteId,
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.vcsAction,
|
||||
properties: vcsSegmentEventProperties('remote', 'push'),
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.vcsAction,
|
||||
properties: vcsEventProperties('remote', 'push'),
|
||||
});
|
||||
|
||||
delete remoteCompareCache[workspaceId];
|
||||
@@ -34,9 +34,9 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error while pushing to remote.';
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.vcsAction,
|
||||
properties: vcsSegmentEventProperties('remote', 'push', errorMessage),
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.vcsAction,
|
||||
properties: vcsEventProperties('remote', 'push', errorMessage),
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Route } from './+types/organization.$organizationId.project.$proje
|
||||
export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
const { projectId, workspaceId } = params;
|
||||
try {
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
invariant(project.remoteId, 'Project is not remote');
|
||||
const { syncItems } = await getSyncItems({ workspaceId });
|
||||
@@ -68,7 +68,7 @@ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
|
||||
export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
const { projectId, workspaceId } = params;
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
invariant(project.remoteId, 'Project is not remote');
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
invariant(typeof projectId === 'string', 'Project Id is required');
|
||||
|
||||
try {
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
const remoteId = project.remoteId;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useWorkspaceLoaderData } from './organization.$organizationId.project.$
|
||||
export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
const { projectId, workspaceId, organizationId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
if (!project) {
|
||||
showResourceNotFoundToast(`Project not found: ${projectId}`);
|
||||
throw redirect(href('/organization/:organizationId/project', { organizationId }));
|
||||
|
||||
@@ -2,7 +2,7 @@ import { href, redirect } from 'react-router';
|
||||
|
||||
import { getMockServiceBinURL } from '~/common/constants';
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { Route } from './+types/organization.$organizationId.project.$proje
|
||||
export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
const { organizationId, projectId, workspaceId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
const workspace = await services.workspace.getById(workspaceId);
|
||||
@@ -62,8 +62,8 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
});
|
||||
}
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.generateCollectionFromMock,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.generateCollectionFromMock,
|
||||
properties: {
|
||||
count_requests: mockRoutes.length,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { href, redirect } from 'react-router';
|
||||
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -16,8 +16,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
|
||||
await services.mockRoute.remove(mockRoute);
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.mockRouteDelete,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.mockRouteDelete,
|
||||
});
|
||||
|
||||
if (isSelected) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import { models, services } from '~/insomnia-data';
|
||||
import { useRootLoaderData } from '~/root';
|
||||
import { useRequestNewMockSendActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.debug.request.new-mock-send';
|
||||
import { useMockRouteUpdateActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.mock-server.mock-route.$mockRouteId.update';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { CodeEditor } from '~/ui/components/.client/codemirror/code-editor';
|
||||
import { Dropdown, DropdownItem, ItemContent } from '~/ui/components/base/dropdown';
|
||||
import { MockResponseHeadersEditor } from '~/ui/components/editors/mock-response-headers-editor';
|
||||
@@ -304,8 +304,8 @@ export const MockRouteRoute = () => {
|
||||
noLint={mockRoute.body?.includes('{{') && mockRoute.body?.includes('}}')}
|
||||
updateFilter={filter => {
|
||||
if (filter) {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.filterCreatedResponseBody,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.filterCreatedResponseBody,
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { href } from 'react-router';
|
||||
|
||||
import type { MockRoute } from '~/insomnia-data';
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -47,8 +47,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
|
||||
await services.mockRoute.update(mockRoute, patch);
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.mockRouteEdit,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.mockRouteEdit,
|
||||
});
|
||||
|
||||
return null;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { href, redirect } from 'react-router';
|
||||
|
||||
import type { MockRoute } from '~/insomnia-data';
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -57,8 +57,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
delete patch.mockServerName;
|
||||
const mockRoute = await services.mockRoute.create({ ...patch, parentId: newMockServer._id });
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.mockRouteCreate,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.mockRouteCreate,
|
||||
});
|
||||
|
||||
return redirect(
|
||||
@@ -78,8 +78,8 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
invariant(mockServer, 'Mock server not found');
|
||||
const mockRoute = await services.mockRoute.create(patch);
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.mockRouteCreate,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.mockRouteCreate,
|
||||
properties: {
|
||||
source: 'from_response',
|
||||
},
|
||||
|
||||
@@ -49,7 +49,7 @@ export interface MockServerLoaderData {
|
||||
export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
const { workspaceId, projectId, organizationId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
if (!project) {
|
||||
showResourceNotFoundToast(`Project not found: ${projectId}`);
|
||||
throw redirect(href('/organization/:organizationId/project', { organizationId }));
|
||||
|
||||
@@ -3,7 +3,7 @@ import { href, redirect } from 'react-router';
|
||||
|
||||
import { importResourcesToWorkspace, scanResources } from '~/common/import';
|
||||
import { models, services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { Route } from './+types/organization.$organizationId.project.$proje
|
||||
export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
const { organizationId, projectId, workspaceId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
|
||||
const apiSpec = await services.apiSpec.getByParentId(workspaceId);
|
||||
@@ -53,8 +53,8 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.generateCollection,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.generateCollection,
|
||||
properties: {
|
||||
count_requests: scannedResources.map(r => r.requests?.length ?? 0).reduce((a, b) => a + b, 0),
|
||||
},
|
||||
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
import { useSpecGenerateRequestCollectionActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.spec.generate-request-collection';
|
||||
import { useSpecUpdateActionFetcher } from '~/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.spec.update';
|
||||
import { useStorageRulesLoaderFetcher } from '~/routes/organization.$organizationId.storage-rules';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { CodeEditor, type CodeEditorHandle } from '~/ui/components/.client/codemirror/code-editor';
|
||||
import { DesignEmptyState } from '~/ui/components/design-empty-state';
|
||||
import { DocumentTab } from '~/ui/components/document-tab';
|
||||
@@ -60,7 +60,7 @@ import type { Route } from './+types/organization.$organizationId.project.$proje
|
||||
export async function clientLoader({ params }: Route.ClientLoaderArgs) {
|
||||
const { organizationId, projectId, workspaceId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
if (!project) {
|
||||
showResourceNotFoundToast(`Project not found: ${projectId}`);
|
||||
throw redirect(href('/organization/:organizationId/project', { organizationId }));
|
||||
@@ -402,8 +402,8 @@ const Component = ({ params }: Route.ComponentProps) => {
|
||||
name: 'Toggle preview',
|
||||
icon: <Icon className="w-3" icon={isSpecPaneOpen ? 'eye' : 'eye-slash'} />,
|
||||
action: () => {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.designerPreviewToggled,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.designerPreviewToggled,
|
||||
properties: {
|
||||
status: !isSpecPaneOpen ? 'open' : 'collapsed',
|
||||
},
|
||||
@@ -492,8 +492,8 @@ const Component = ({ params }: Route.ComponentProps) => {
|
||||
{isGenerateMockServersWithAIEnabled && (
|
||||
<Button
|
||||
onPress={() => {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.designerGenerateMockClicked,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.designerGenerateMockClicked,
|
||||
});
|
||||
setNewMockServerModalOpen(true);
|
||||
}}
|
||||
@@ -510,8 +510,8 @@ const Component = ({ params }: Route.ComponentProps) => {
|
||||
className="flex h-full items-center justify-center gap-2 rounded-xs px-2 text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
|
||||
onChange={value => {
|
||||
setIsSpecPaneOpen(value);
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.designerPreviewToggled,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.designerPreviewToggled,
|
||||
properties: {
|
||||
status: !value ? 'open' : 'collapsed',
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { href, redirect } from 'react-router';
|
||||
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
|
||||
await services.unitTestSuite.remove(unitTestSuite);
|
||||
|
||||
window.main.trackSegmentEvent({ event: SegmentEvent.testSuiteDelete });
|
||||
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.testSuiteDelete });
|
||||
|
||||
return redirect(
|
||||
href(`/organization/:organizationId/project/:projectId/workspace/:workspaceId/test`, {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { database } from '~/common/database';
|
||||
import type { UnitTest } from '~/insomnia-data';
|
||||
import { models, services } from '~/insomnia-data';
|
||||
import { getSendRequestCallback } from '~/network/unit-test-feature';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
results,
|
||||
parentId: workspaceId,
|
||||
});
|
||||
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestRunAll, properties: { organizationId, projectId } });
|
||||
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestRunAll, properties: { organizationId, projectId } });
|
||||
|
||||
return redirect(
|
||||
href(
|
||||
@@ -93,7 +93,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
results,
|
||||
parentId: workspaceId,
|
||||
});
|
||||
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestRunAll, properties: { organizationId, projectId } });
|
||||
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestRunAll, properties: { organizationId, projectId } });
|
||||
|
||||
return redirect(
|
||||
href(
|
||||
|
||||
@@ -3,7 +3,7 @@ import { href } from 'react-router';
|
||||
import { database } from '~/common/database';
|
||||
import type { UnitTest } from '~/insomnia-data';
|
||||
import { models, services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -18,7 +18,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
invariant(unitTest, 'Test not found');
|
||||
|
||||
await services.unitTest.remove(unitTest);
|
||||
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestDelete });
|
||||
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestDelete });
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { database } from '~/common/database';
|
||||
import type { UnitTest } from '~/insomnia-data';
|
||||
import { models, services } from '~/insomnia-data';
|
||||
import { getSendRequestCallback } from '~/network/unit-test-feature';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
results,
|
||||
parentId: unitTest.parentId,
|
||||
});
|
||||
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestRun, properties: { organizationId, projectId } });
|
||||
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestRun, properties: { organizationId, projectId } });
|
||||
|
||||
return redirect(
|
||||
href(
|
||||
@@ -94,7 +94,7 @@ export async function clientAction({ params }: Route.ClientActionArgs) {
|
||||
results,
|
||||
parentId: unitTest.parentId,
|
||||
});
|
||||
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestRun, properties: { organizationId, projectId } });
|
||||
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestRun, properties: { organizationId, projectId } });
|
||||
|
||||
return redirect(
|
||||
href(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { href } from 'react-router';
|
||||
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -22,7 +22,7 @@ expect(response1.status).to.equal(200);`,
|
||||
name,
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({ event: SegmentEvent.unitTestCreate });
|
||||
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.unitTestCreate });
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { href, redirect } from 'react-router';
|
||||
|
||||
import { services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
|
||||
name,
|
||||
});
|
||||
|
||||
window.main.trackSegmentEvent({ event: SegmentEvent.testSuiteCreate });
|
||||
window.main.trackAnalyticsEvent({ event: AnalyticsEvent.testSuiteCreate });
|
||||
|
||||
return redirect(
|
||||
href('/organization/:organizationId/project/:projectId/workspace/:workspaceId/test/test-suite/:testSuiteId', {
|
||||
|
||||
@@ -88,7 +88,7 @@ const workspaceFileIssueModalText = {
|
||||
export async function clientLoader({ params, request }: Route.ClientLoaderArgs) {
|
||||
const { organizationId, projectId, workspaceId } = params;
|
||||
|
||||
const activeProject = await services.project.getById(projectId);
|
||||
const activeProject = await services.project.get(projectId);
|
||||
if (!activeProject) {
|
||||
showResourceNotFoundToast(`Project not found: ${projectId}`);
|
||||
throw redirect(href('/organization/:organizationId/project', { organizationId }));
|
||||
@@ -281,7 +281,7 @@ export async function clientLoader({ params, request }: Route.ClientLoaderArgs)
|
||||
return collection;
|
||||
}
|
||||
|
||||
const userSession = await services.userSession.getOrCreate();
|
||||
const userSession = await services.userSession.get();
|
||||
const isLoggedInIsCloudProjectAndIsNotGitRepo = userSession.id && activeProject.remoteId && !gitRepository;
|
||||
let vcsVersion = null;
|
||||
if (isLoggedInIsCloudProjectAndIsNotGitRepo) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { href, redirect } from 'react-router';
|
||||
|
||||
import type { Project, Workspace } from '~/insomnia-data';
|
||||
import { models, services } from '~/insomnia-data';
|
||||
import { SegmentEvent } from '~/ui/analytics';
|
||||
import { AnalyticsEvent } from '~/ui/analytics';
|
||||
import uiEventBus, { CLOUD_SYNC_FILE_CHANGE } from '~/ui/event-bus';
|
||||
import { invariant } from '~/utils/invariant';
|
||||
import { createFetcherSubmitHook } from '~/utils/router';
|
||||
@@ -52,8 +52,8 @@ async function deleteWorkspace(workspace: Workspace | null, project: Project | n
|
||||
await deleteWorkspaceFromLocal(workspace);
|
||||
|
||||
if (workspace.scope === 'mock-server') {
|
||||
window.main.trackSegmentEvent({
|
||||
event: SegmentEvent.mockDelete,
|
||||
window.main.trackAnalyticsEvent({
|
||||
event: AnalyticsEvent.mockDelete,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ async function deleteWorkspace(workspace: Workspace | null, project: Project | n
|
||||
export async function clientAction({ request, params }: Route.ClientActionArgs) {
|
||||
const { organizationId, projectId } = params;
|
||||
|
||||
const project = await services.project.getById(projectId);
|
||||
const project = await services.project.get(projectId);
|
||||
invariant(project, 'Project not found');
|
||||
const formData = await request.formData();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user