Skip to content

Commit

Permalink
[AXON-109] Expose toggle switch to push branch to remote (Start work) (
Browse files Browse the repository at this point in the history
…#137)

* Expose toggle switch to push branch to origin (Start work)
* Updated CHANGELOG
  • Loading branch information
marcomura authored Feb 21, 2025
1 parent 1eb76a1 commit e296026
Show file tree
Hide file tree
Showing 15 changed files with 194 additions and 37 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## What's new in 3.4.8

### Features

- Added a new toggle switch in 'Start work' page to choose if the new branch should be automatically pushed to remote.

## What's new in 3.4.7

### Features
Expand Down
103 changes: 103 additions & 0 deletions src/analytics.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { viewScreenEvent } from './analytics';

interface MockedData {
getFirstAAID_value?: boolean;
}

const mockedData: MockedData = {};

jest.mock('./container', () => ({
Container: { siteManager: { getFirstAAID: () => mockedData.getFirstAAID_value } },
}));

function setProcessPlatform(platform: NodeJS.Platform) {
Object.defineProperty(process, 'platform', {
value: platform,
writable: false,
});
}

describe('viewScreenEvent', () => {
const originalPlatform = process.platform;

beforeEach(() => {
setProcessPlatform('win32');
mockedData.getFirstAAID_value = true;
});

afterAll(() => {
setProcessPlatform(originalPlatform);
});

it('should create a screen event with the correct screen name', async () => {
const screenName = 'testScreen';
const event = await viewScreenEvent(screenName);
expect(event.name).toEqual(screenName);
expect(event.screenEvent.attributes).toBeUndefined();
});

it('should exclude from activity if screen name is atlascodeWelcomeScreen', async () => {
const screenName = 'atlascodeWelcomeScreen';
const event = await viewScreenEvent(screenName);
expect(event.screenEvent.attributes.excludeFromActivity).toBeTruthy();
});

it('should include site information if provided (cloud)', async () => {
const screenName = 'testScreen';
const site: any = {
id: 'siteId',
product: { name: 'Jira', key: 'jira' },
isCloud: true,
};
const event = await viewScreenEvent(screenName, site);
expect(event.screenEvent.attributes.instanceType).toEqual('cloud');
expect(event.screenEvent.attributes.hostProduct).toEqual('Jira');
});

it('should include site information if provided (server)', async () => {
const screenName = 'testScreen';
const site: any = {
id: 'siteId',
product: { name: 'Jira', key: 'jira' },
isCloud: false,
};
const event = await viewScreenEvent(screenName, site);
expect(event.screenEvent.attributes.instanceType).toEqual('server');
expect(event.screenEvent.attributes.hostProduct).toEqual('Jira');
});

it('should include product information if provided', async () => {
const screenName = 'testScreen';
const product = { name: 'Bitbucket', key: 'bitbucket' };
const event = await viewScreenEvent(screenName, undefined, product);
expect(event.screenEvent.attributes.hostProduct).toEqual('Bitbucket');
});

it('should set platform based on process.platform (win32)', async () => {
setProcessPlatform('win32');
const screenName = 'testScreen';
const event = await viewScreenEvent(screenName);
expect(event.screenEvent.platform).toEqual('windows');
});

it('should set platform based on process.platform (darwin)', async () => {
setProcessPlatform('darwin');
const screenName = 'testScreen';
const event = await viewScreenEvent(screenName);
expect(event.screenEvent.platform).toEqual('mac');
});

it('should set platform based on process.platform (linux)', async () => {
setProcessPlatform('linux');
const screenName = 'testScreen';
const event = await viewScreenEvent(screenName);
expect(event.screenEvent.platform).toEqual('linux');
});

it('should set platform based on process.platform (aix)', async () => {
setProcessPlatform('aix');
const screenName = 'testScreen';
const event = await viewScreenEvent(screenName);
expect(event.screenEvent.platform).toEqual('desktop');
});
});
53 changes: 27 additions & 26 deletions src/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const Registry = {
};

class AnalyticsPlatform {
private static nodeJsPlatformMapping = {
private static nodeJsPlatformMapping: Record<NodeJS.Platform, string> = {
aix: 'desktop',
android: 'android',
darwin: 'mac',
Expand All @@ -31,9 +31,11 @@ class AnalyticsPlatform {
sunos: 'desktop',
win32: 'windows',
cygwin: 'windows',
haiku: 'unknown',
netbsd: 'unknown',
};

static for(p: string): string {
static for(p: NodeJS.Platform): string {
return this.nodeJsPlatformMapping[p] || 'unknown';
}
}
Expand Down Expand Up @@ -107,8 +109,13 @@ export async function issueCommentEvent(site: DetailedSiteInfo): Promise<TrackEv
return instanceTrackEvent(site, 'created', 'issueComment');
}

export async function issueWorkStartedEvent(site: DetailedSiteInfo): Promise<TrackEvent> {
return instanceTrackEvent(site, 'workStarted', 'issue');
export async function issueWorkStartedEvent(
site: DetailedSiteInfo,
pushBranchToRemoteChecked: boolean,
): Promise<TrackEvent> {
const attributesObject = instanceType({}, site);
attributesObject.attributes.pushBranchToRemoteChecked = pushBranchToRemoteChecked;
return instanceTrackEvent(site, 'workStarted', 'issue', attributesObject);
}

export async function issueUpdatedEvent(
Expand Down Expand Up @@ -164,7 +171,7 @@ export async function prCommentEvent(site: DetailedSiteInfo): Promise<TrackEvent
}

export async function prTaskEvent(site: DetailedSiteInfo, source: string): Promise<TrackEvent> {
const attributesObject: any = instanceType({}, site);
const attributesObject = instanceType({}, site);
attributesObject.attributes.source = source;
return trackEvent('created', 'pullRequestComment', attributesObject);
}
Expand Down Expand Up @@ -657,33 +664,27 @@ function tenantOrNull<T>(e: Object, tenantId?: string): T {
return newObj as T;
}

function instanceType(eventProps: Object, site?: DetailedSiteInfo, product?: Product): Object {
let attrs: Object | undefined = undefined;
const newObj = eventProps;

function instanceType(
eventProps: Record<string, any>,
site?: DetailedSiteInfo,
product?: Product,
): Record<string, any> {
if (product) {
attrs = { hostProduct: product.name };
eventProps.attributes = eventProps.attributes || {};
eventProps.attributes.hostProduct = product.name;
}

if (site && !isEmptySiteInfo(site)) {
const instanceType: string = site.isCloud ? 'cloud' : 'server';
attrs = { instanceType: instanceType, hostProduct: site.product.name };
eventProps.attributes = eventProps.attributes || {};
eventProps.attributes.instanceType = site.isCloud ? 'cloud' : 'server';
eventProps.attributes.hostProduct = site.product.name;
}

if (attrs) {
newObj['attributes'] = { ...newObj['attributes'], ...attrs };
}

return newObj;
return eventProps;
}

function excludeFromActivity(eventProps: Object): Object {
const newObj = eventProps;

if (newObj['attributes']) {
newObj['attributes'] = { ...newObj['attributes'], ...{ excludeFromActivity: true } };
} else {
Object.assign(newObj, { attributes: { excludeFromActivity: true } });
}
return newObj;
function excludeFromActivity(eventProps: Record<string, any>): Record<string, any> {
eventProps.attributes = eventProps.attributes || {};
eventProps.attributes.excludeFromActivity = true;
return eventProps;
}
1 change: 1 addition & 0 deletions src/ipc/issueActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export interface StartWorkAction extends Action {
remoteName: string;
setupJira: boolean;
setupBitbucket: boolean;
pushBranchToRemote: boolean;
}

export interface OpenStartWorkPageAction extends Action {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/analyticsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface AnalyticsApi {
fireIssueTransitionedEvent(site: DetailedSiteInfo, issueKey: string): Promise<void>;
fireIssueUrlCopiedEvent(): Promise<void>;
fireIssueCommentEvent(site: DetailedSiteInfo): Promise<void>;
fireIssueWorkStartedEvent(site: DetailedSiteInfo): Promise<void>;
fireIssueWorkStartedEvent(site: DetailedSiteInfo, pushBranchToRemoteChecked: boolean): Promise<void>;
fireIssueUpdatedEvent(site: DetailedSiteInfo, issueKey: string, fieldName: string, fieldKey: string): Promise<void>;
fireStartIssueCreationEvent(source: string, product: Product): Promise<void>;
fireBBIssueCreatedEvent(site: DetailedSiteInfo): Promise<void>;
Expand Down
1 change: 1 addition & 0 deletions src/lib/ipc/fromUI/startWork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface StartRequestAction {
sourceBranch: Branch;
targetBranch: string;
upstream: string;
pushBranchToRemote: boolean;
}

export interface OpenSettingsAction {
Expand Down
1 change: 1 addition & 0 deletions src/lib/webview/controller/startwork/startWorkActionApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface StartWorkActionApi {
destinationBranch: string,
sourceBranch: Branch,
remote: string,
pushBranchToRemote: boolean,
): Promise<void>;
closePage(): void;
getStartWorkConfig(): StartWorkBranchTemplate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export class StartWorkWebviewController implements WebviewController<StartWorkIs
msg.targetBranch,
msg.sourceBranch,
msg.upstream,
msg.pushBranchToRemote,
);
}
this.postMessage({
Expand All @@ -142,7 +143,7 @@ export class StartWorkWebviewController implements WebviewController<StartWorkIs
branch: msg.branchSetupEnabled ? msg.targetBranch : undefined,
upstream: msg.branchSetupEnabled ? msg.upstream : undefined,
});
this.analytics.fireIssueWorkStartedEvent(this.initData.issue.siteDetails);
this.analytics.fireIssueWorkStartedEvent(this.initData.issue.siteDetails, msg.pushBranchToRemote);
} catch (e) {
this.logger.error(new Error(`error executing start work action: ${e}`));
this.postMessage({
Expand Down
27 changes: 27 additions & 0 deletions src/react/atlascode/startwork/StartWorkPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ const StartWorkPage: React.FunctionComponent = () => {

const [transitionIssueEnabled, setTransitionIssueEnabled] = useState(true);
const [branchSetupEnabled, setbranchSetupEnabled] = useState(true);
const [pushBranchEnabled, setPushBranchEnabled] = useState(true);
const [transition, setTransition] = useState<Transition>(emptyTransition);
const [repository, setRepository] = useState<RepoData>(emptyRepoData);
const [branchType, setBranchType] = useState<BranchType>(emptyPrefix);
Expand All @@ -120,6 +121,8 @@ const StartWorkPage: React.FunctionComponent = () => {
[branchSetupEnabled],
);

const togglePushBranchEnabled = useCallback(() => setPushBranchEnabled(!pushBranchEnabled), [pushBranchEnabled]);

const handleTransitionChange = useCallback(
(event: React.ChangeEvent<{ name?: string | undefined; value: any }>) => {
setTransition(event.target.value);
Expand Down Expand Up @@ -248,6 +251,7 @@ const StartWorkPage: React.FunctionComponent = () => {
sourceBranch,
localBranch,
upstream,
pushBranchEnabled,
);
setSubmitState('submit-success');
setSubmitResponse(response);
Expand All @@ -264,6 +268,7 @@ const StartWorkPage: React.FunctionComponent = () => {
sourceBranch,
localBranch,
upstream,
pushBranchEnabled,
]);

const handleOpenSettings = useCallback(() => {
Expand Down Expand Up @@ -704,6 +709,28 @@ const StartWorkPage: React.FunctionComponent = () => {
</Grid>
</Collapse>
</Grid>
<Grid item hidden={submitState === 'submit-success'}>
<Divider />
</Grid>
<Grid item hidden={submitState === 'submit-success'}>
<Grid container spacing={1} direction="row">
<Grid item>
<Switch
color="primary"
size="small"
checked={pushBranchEnabled}
onClick={togglePushBranchEnabled}
/>
</Grid>
<Grid item>
<Typography variant="h4">
<Box fontWeight="fontWeightBold">
Push the new branch to remote
</Box>
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item hidden={submitState === 'submit-success'}>
<Button
variant="contained"
Expand Down
3 changes: 3 additions & 0 deletions src/react/atlascode/startwork/startWorkController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface StartWorkControllerApi {
sourceBranch: Branch,
targetBranch: string,
upstream: string,
pushBranchToRemote: boolean,
) => Promise<{ transistionStatus?: string; branch?: string; upstream?: string }>;
closePage: () => void;
openJiraIssue: () => void;
Expand Down Expand Up @@ -122,6 +123,7 @@ export function useStartWorkController(): [StartWorkState, StartWorkControllerAp
sourceBranch: Branch,
targetBranch: string,
upstream: string,
pushBranchToRemote: boolean,
): Promise<StartWorkResponseMessage> => {
return new Promise<StartWorkResponseMessage>((resolve, reject) => {
(async () => {
Expand All @@ -136,6 +138,7 @@ export function useStartWorkController(): [StartWorkState, StartWorkControllerAp
sourceBranch,
targetBranch,
upstream,
pushBranchToRemote,
},
StartWorkMessageType.StartWorkResponse,
ConnectionTimeout,
Expand Down
4 changes: 2 additions & 2 deletions src/vscAnalyticsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ export class VSCAnalyticsApi implements AnalyticsApi {
});
}

public async fireIssueWorkStartedEvent(site: DetailedSiteInfo): Promise<void> {
return issueWorkStartedEvent(site).then((e) => {
public async fireIssueWorkStartedEvent(site: DetailedSiteInfo, pushBranchToRemoteChecked: boolean): Promise<void> {
return issueWorkStartedEvent(site, pushBranchToRemoteChecked).then((e) => {
this._analyticsClient.sendTrackEvent(e);
});
}
Expand Down
7 changes: 5 additions & 2 deletions src/webview/startwork/vscStartWorkActionApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class VSCStartWorkActionApi implements StartWorkActionApi {
destinationBranch: string,
sourceBranch: Branch,
remote: string,
pushBranchToRemote: boolean,
): Promise<void> {
const scm = Container.bitbucketContext.getRepositoryScm(wsRepo.rootUri)!;

Expand All @@ -74,8 +75,10 @@ export class VSCStartWorkActionApi implements StartWorkActionApi {
true,
`${sourceBranch.type === RefType.RemoteHead ? 'remotes/' : ''}${sourceBranch.name}`,
);
await scm.push(remote, destinationBranch, true);
return;

if (pushBranchToRemote) {
await scm.push(remote, destinationBranch, true);
}
}

getStartWorkConfig(): StartWorkBranchTemplate {
Expand Down
1 change: 1 addition & 0 deletions src/webviews/components/issue/StartWorkPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ export default class StartWorkPage extends WebviewComponent<Emit, Accept, {}, St
transition: this.state.transition,
setupJira: this.state.jiraSetupEnabled,
setupBitbucket: this.isEmptyRepo(this.state.repo) ? false : this.state.bitbucketSetupEnabled,
pushBranchToRemote: false,
});
};

Expand Down
Loading

0 comments on commit e296026

Please sign in to comment.