mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 22:30:15 +00:00
Git Sync Events (#4365)
* Cleanup old plugin events * Add VCS lifecyle events * Git action events * Apply Opender's Suggestions Co-authored-by: Opender Singh <opender.singh@konghq.com> * Apply review changes for stage/unstage & rollback * Update error messages for clone events Co-authored-by: Opender Singh <opender.singh@konghq.com>
This commit is contained in:
parent
1ac24bf980
commit
35c451b803
@ -117,8 +117,6 @@ let segmentClient: Analytics | null = null;
|
||||
export enum SegmentEvent {
|
||||
collectionCreate = 'Collection Created',
|
||||
documentCreate = 'Document Created',
|
||||
pluginExportLoadAllWokspace = 'Plugin export loading all workspace',
|
||||
pluginExportLoadWorkspacesInProject = 'Plugin export loading workspaces for active project',
|
||||
requestCreate = 'Request Created',
|
||||
requestExecute = 'Request Executed',
|
||||
projectLocalCreate = 'Local Project Created',
|
||||
@ -129,6 +127,26 @@ export enum SegmentEvent {
|
||||
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',
|
||||
}
|
||||
|
||||
type PushPull = 'push' | 'pull';
|
||||
|
||||
export function vcsSegmentEventProperties(
|
||||
type: 'git',
|
||||
action: PushPull | `force_${PushPull}` |
|
||||
'create_branch' | 'merge_branch' | 'delete_branch' | 'checkout_branch' |
|
||||
'commit' | 'stage_all' | 'stage' | 'unstage_all' | 'unstage' | 'rollback' | 'rollback_all' |
|
||||
'update' | 'setup' | 'clone',
|
||||
error?: string
|
||||
) {
|
||||
return {
|
||||
'type': type,
|
||||
'action': action,
|
||||
'error': error,
|
||||
};
|
||||
}
|
||||
|
||||
export async function trackSegmentEvent(event: SegmentEvent, properties?: Record<string, any>) {
|
||||
@ -152,8 +170,7 @@ export async function trackSegmentEvent(event: SegmentEvent, properties?: Record
|
||||
}
|
||||
|
||||
const anonymousId = await getDeviceId();
|
||||
// TODO: This currently always returns an empty string in the main process
|
||||
// This is due to the session data being stored in localStorage
|
||||
// This may return an empty string or undefined when a user is not logged in
|
||||
const userId = getAccountId();
|
||||
segmentClient.track({
|
||||
anonymousId,
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { SegmentEvent, trackSegmentEvent } from '../../common/analytics';
|
||||
import { exportWorkspacesData, exportWorkspacesHAR } from '../../common/export';
|
||||
import type { ImportRawConfig } from '../../common/import';
|
||||
import { importRaw, importUri } from '../../common/import';
|
||||
@ -29,10 +28,11 @@ const buildImportRawConfig = (options: PluginImportOptions, activeProjectId: str
|
||||
|
||||
const getWorkspaces = (activeProjectId?: string) => {
|
||||
if (activeProjectId) {
|
||||
trackSegmentEvent(SegmentEvent.pluginExportLoadWorkspacesInProject);
|
||||
return models.workspace.findByParentId(activeProjectId);
|
||||
} else {
|
||||
trackSegmentEvent(SegmentEvent.pluginExportLoadAllWokspace);
|
||||
// This code path was kept in case there was ever a time when the app wouldn't have an active project.
|
||||
// In over 5 months of monitoring in production, we never saw this happen.
|
||||
// Keeping it for defensive purposes, but it's not clear if it's necessary.
|
||||
return models.workspace.all();
|
||||
}
|
||||
};
|
||||
|
@ -4,7 +4,7 @@ import React, { Fragment, PureComponent, ReactNode } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { trackEvent } from '../../../common/analytics';
|
||||
import { SegmentEvent, trackEvent, trackSegmentEvent, vcsSegmentEventProperties } from '../../../common/analytics';
|
||||
import { AUTOBIND_CFG } from '../../../common/constants';
|
||||
import { database as db } from '../../../common/database';
|
||||
import { docsGitSync } from '../../../common/documentation';
|
||||
@ -130,11 +130,13 @@ class GitSyncDropdown extends PureComponent<Props, State> {
|
||||
|
||||
try {
|
||||
await vcs.pull(gitRepository.credentials);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', 'pull'));
|
||||
} catch (err) {
|
||||
showError({
|
||||
title: 'Error Pulling Repository',
|
||||
error: err,
|
||||
});
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', 'pull', err.message));
|
||||
}
|
||||
|
||||
await db.flushChanges(bufferId);
|
||||
@ -186,6 +188,7 @@ class GitSyncDropdown extends PureComponent<Props, State> {
|
||||
|
||||
try {
|
||||
await vcs.push(gitRepository.credentials, force);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', force ? 'force_push' : 'push'));
|
||||
} catch (err) {
|
||||
if (err.code === 'PushRejectedError') {
|
||||
this._dropdown?.hide();
|
||||
@ -203,6 +206,7 @@ class GitSyncDropdown extends PureComponent<Props, State> {
|
||||
title: 'Error Pushing Repository',
|
||||
error: err,
|
||||
});
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', force ? 'force_push' : 'push', err.message));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import classnames from 'classnames';
|
||||
import React, { Fragment, PureComponent } from 'react';
|
||||
|
||||
import { SegmentEvent, trackSegmentEvent, vcsSegmentEventProperties } from '../../../common/analytics';
|
||||
import { AUTOBIND_CFG } from '../../../common/constants';
|
||||
import { database as db } from '../../../common/database';
|
||||
import type { GitRepository } from '../../../models/git-repository';
|
||||
@ -113,6 +114,7 @@ export class GitBranchesModal extends PureComponent<Props, State> {
|
||||
const { vcs } = this.props;
|
||||
const { newBranchName } = this.state;
|
||||
await vcs.checkout(newBranchName);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', 'create_branch'));
|
||||
await this._refreshState({
|
||||
newBranchName: '',
|
||||
});
|
||||
@ -125,6 +127,7 @@ export class GitBranchesModal extends PureComponent<Props, State> {
|
||||
await vcs.merge(branch);
|
||||
// Apparently merge doesn't update the working dir so need to checkout too
|
||||
await this._handleCheckout(branch);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', 'merge_branch'));
|
||||
});
|
||||
}
|
||||
|
||||
@ -132,6 +135,7 @@ export class GitBranchesModal extends PureComponent<Props, State> {
|
||||
await this._errorHandler(async () => {
|
||||
const { vcs } = this.props;
|
||||
await vcs.deleteBranch(branch);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', 'delete_branch'));
|
||||
await this._refreshState();
|
||||
});
|
||||
}
|
||||
@ -150,6 +154,7 @@ export class GitBranchesModal extends PureComponent<Props, State> {
|
||||
const { vcs, handleInitializeEntities } = this.props;
|
||||
const bufferId = await db.bufferChanges();
|
||||
await vcs.checkout(branch);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', 'checkout_branch'));
|
||||
await db.flushChanges(bufferId, true);
|
||||
await handleInitializeEntities();
|
||||
await this._refreshState();
|
||||
|
@ -4,6 +4,7 @@ import path from 'path';
|
||||
import React, { Fragment, PureComponent } from 'react';
|
||||
import YAML from 'yaml';
|
||||
|
||||
import { SegmentEvent, trackSegmentEvent, vcsSegmentEventProperties } from '../../../common/analytics';
|
||||
import { AUTOBIND_CFG } from '../../../common/constants';
|
||||
import { database as db } from '../../../common/database';
|
||||
import { strings } from '../../../common/strings';
|
||||
@ -96,6 +97,7 @@ export class GitStagingModal extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
await vcs.commit(message);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', 'commit'));
|
||||
this.modal?.hide();
|
||||
|
||||
if (typeof this.onCommit === 'function') {
|
||||
@ -120,6 +122,7 @@ export class GitStagingModal extends PureComponent<Props, State> {
|
||||
newItems[p].staged = doStage || forceAdd;
|
||||
}
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', doStage ? 'stage_all' : 'unstage_all'));
|
||||
this.setState({
|
||||
items: newItems,
|
||||
});
|
||||
@ -134,6 +137,7 @@ export class GitStagingModal extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
newItems[gitPath].staged = !newItems[gitPath].staged;
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', newItems[gitPath].staged ? 'stage' : 'unstage'));
|
||||
this.setState({
|
||||
items: newItems,
|
||||
});
|
||||
@ -306,6 +310,16 @@ export class GitStagingModal extends PureComponent<Props, State> {
|
||||
await this._refresh();
|
||||
}
|
||||
|
||||
async _handleRollbackSingle(item: Item) {
|
||||
await this._handleRollback([item]);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', 'rollback'));
|
||||
}
|
||||
|
||||
async _handleRollbackAll(items: Item[]) {
|
||||
await this._handleRollback(items);
|
||||
trackSegmentEvent(SegmentEvent.vcsAction, vcsSegmentEventProperties('git', 'rollback_all'));
|
||||
}
|
||||
|
||||
renderItem(item: Item) {
|
||||
const { path: gitPath, staged, editable } = item;
|
||||
const docName = this.statusNames[gitPath] || 'n/a';
|
||||
@ -328,7 +342,7 @@ export class GitStagingModal extends PureComponent<Props, State> {
|
||||
{item.editable && <Tooltip message={item.added ? 'Delete' : 'Rollback'}>
|
||||
<button
|
||||
className="btn btn--micro space-right"
|
||||
onClick={() => this._handleRollback([item])}
|
||||
onClick={() => this._handleRollbackSingle(item)}
|
||||
>
|
||||
<i className={classnames('fa', item.added ? 'fa-trash' : 'fa-undo')} />
|
||||
</button>
|
||||
@ -351,7 +365,7 @@ export class GitStagingModal extends PureComponent<Props, State> {
|
||||
<strong>{title}</strong>
|
||||
<PromptButton
|
||||
className="btn pull-right btn--micro"
|
||||
onClick={() => this._handleRollback(items)}
|
||||
onClick={() => this._handleRollbackAll(items)}
|
||||
>
|
||||
{rollbackLabel}
|
||||
</PromptButton>
|
||||
|
@ -425,7 +425,7 @@ describe('git', () => {
|
||||
const alertArgs = getAndClearShowAlertMockArgs();
|
||||
expect(alertArgs.title).toBe('Setup Problem');
|
||||
expect(alertArgs.message).toBe(
|
||||
'This repository already contains a workspace; create a fresh clone from the dashboard.',
|
||||
'This repository is already connected to Insomnia; try creating a clone from the dashboard instead.',
|
||||
);
|
||||
// Ensure activity is activated
|
||||
expect(store.getActions()).toEqual([
|
||||
|
@ -3,7 +3,7 @@ import path from 'path';
|
||||
import React, { ReactNode } from 'react';
|
||||
import YAML from 'yaml';
|
||||
|
||||
import { trackEvent } from '../../../common/analytics';
|
||||
import { SegmentEvent, trackEvent, trackSegmentEvent, vcsSegmentEventProperties } from '../../../common/analytics';
|
||||
import { database as db } from '../../../common/database';
|
||||
import { strings } from '../../../common/strings';
|
||||
import * as models from '../../../models';
|
||||
@ -29,10 +29,12 @@ export type UpdateGitRepositoryCallback = (arg0: { gitRepository: GitRepository
|
||||
* */
|
||||
export const updateGitRepository: UpdateGitRepositoryCallback = ({ gitRepository }) => {
|
||||
return () => {
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncStart, vcsSegmentEventProperties('git', 'update'));
|
||||
showModal(GitRepositorySettingsModal, {
|
||||
gitRepository,
|
||||
onSubmitEdits: async gitRepoPatch => {
|
||||
await models.gitRepository.update(gitRepository, gitRepoPatch);
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'update'));
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -47,6 +49,7 @@ export type SetupGitRepositoryCallback = (arg0: {
|
||||
* */
|
||||
export const setupGitRepository: SetupGitRepositoryCallback = ({ createFsClient, workspace }) => {
|
||||
return dispatch => {
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncStart, vcsSegmentEventProperties('git', 'setup'));
|
||||
showModal(GitRepositorySettingsModal, {
|
||||
gitRepository: null,
|
||||
onSubmitEdits: async gitRepoPatch => {
|
||||
@ -67,6 +70,7 @@ export const setupGitRepository: SetupGitRepositoryCallback = ({ createFsClient,
|
||||
message: err.message,
|
||||
error: err,
|
||||
});
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'setup', err.message));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -79,13 +83,17 @@ export const setupGitRepository: SetupGitRepositoryCallback = ({ createFsClient,
|
||||
showAlert({
|
||||
title: 'Setup Problem',
|
||||
message:
|
||||
'This repository already contains a workspace; create a fresh clone from the dashboard.',
|
||||
'This repository is already connected to Insomnia; try creating a clone from the dashboard instead.',
|
||||
});
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'setup', 'existing insomnia data'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await createGitRepository(workspace._id, gitRepoPatch);
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'setup'));
|
||||
} catch (err) {
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'setup', err.message));
|
||||
} finally {
|
||||
dispatch(loadStop());
|
||||
}
|
||||
@ -148,6 +156,8 @@ export const cloneGitRepository = ({ createFsClient }: {
|
||||
return (dispatch, getState: () => RootState) => {
|
||||
// TODO: in the future we should ask which project to clone into...?
|
||||
const activeProject = selectActiveProject(getState());
|
||||
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncStart, vcsSegmentEventProperties('git', 'clone'));
|
||||
showModal(GitRepositorySettingsModal, {
|
||||
gitRepository: null,
|
||||
onSubmitEdits: async repoSettingsPatch => {
|
||||
@ -169,6 +179,7 @@ export const cloneGitRepository = ({ createFsClient }: {
|
||||
message: originalUriError.message,
|
||||
});
|
||||
dispatch(loadStop());
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'clone', originalUriError.message));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -188,6 +199,7 @@ export const cloneGitRepository = ({ createFsClient }: {
|
||||
message: `Failed to clone with original url (${repoSettingsPatch.uri}): ${originalUriError.message};\n\nAlso failed to clone with \`.git\` suffix added (${dotGitUri}): ${dotGitError.message}`,
|
||||
});
|
||||
dispatch(loadStop());
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'clone', dotGitError.message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -196,6 +208,7 @@ export const cloneGitRepository = ({ createFsClient }: {
|
||||
if (!(await containsInsomniaWorkspaceDir(fsClient))) {
|
||||
dispatch(noDocumentFound(repoSettingsPatch));
|
||||
dispatch(loadStop());
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'clone', 'no directory found'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -205,12 +218,14 @@ export const cloneGitRepository = ({ createFsClient }: {
|
||||
if (workspaces.length === 0) {
|
||||
dispatch(noDocumentFound(repoSettingsPatch));
|
||||
dispatch(loadStop());
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'clone', 'no workspaces found'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (workspaces.length > 1) {
|
||||
cloneProblem('Multiple workspaces found in repository; expected one.');
|
||||
dispatch(loadStop());
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'clone', 'multiple workspaces found'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -229,6 +244,7 @@ export const cloneGitRepository = ({ createFsClient }: {
|
||||
</>,
|
||||
);
|
||||
dispatch(loadStop());
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'clone', 'workspace already exists'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -271,6 +287,7 @@ export const cloneGitRepository = ({ createFsClient }: {
|
||||
// Flush DB changes
|
||||
await db.flushChanges(bufferId);
|
||||
dispatch(loadStop());
|
||||
trackSegmentEvent(SegmentEvent.vcsSyncComplete, vcsSegmentEventProperties('git', 'clone'));
|
||||
},
|
||||
});
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user