Skip to content

Commit 49249fe

Browse files
authored
feat(auth): add create oauth app link to enterprise route (#1119)
* feat(auth): add `create oauth app` link to enterprise route * feat(auth): add `create oauth app` link to enterprise route * feat(auth): add `create oauth app` link to enterprise route * feat(auth): add `create oauth app` link to enterprise route
1 parent 95d931a commit 49249fe

File tree

5 files changed

+131
-1
lines changed

5 files changed

+131
-1
lines changed

src/routes/LoginEnterprise.test.tsx

+37
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { fireEvent, render, screen } from '@testing-library/react';
22
import { MemoryRouter } from 'react-router-dom';
33
import * as TestRenderer from 'react-test-renderer';
44
const { ipcRenderer } = require('electron');
5+
import { shell } from 'electron';
56
import { mockedEnterpriseAccounts } from '../__mocks__/mockedData';
67
import { AppContext } from '../context/App';
78
import type { AuthState } from '../types';
@@ -14,6 +15,8 @@ jest.mock('react-router-dom', () => ({
1415
}));
1516

1617
describe('routes/LoginEnterprise.tsx', () => {
18+
const openExternalMock = jest.spyOn(shell, 'openExternal');
19+
1720
const mockAccounts: AuthState = {
1821
enterpriseAccounts: [],
1922
user: null,
@@ -75,6 +78,40 @@ describe('routes/LoginEnterprise.tsx', () => {
7578
expect(validate(values).clientSecret).toBe('Invalid client secret.');
7679
});
7780

81+
describe("'Create new OAuth App' button", () => {
82+
it('should be disabled if no hostname configured', async () => {
83+
render(
84+
<AppContext.Provider value={{ accounts: mockAccounts }}>
85+
<MemoryRouter>
86+
<LoginEnterpriseRoute />
87+
</MemoryRouter>
88+
</AppContext.Provider>,
89+
);
90+
91+
fireEvent.click(screen.getByText('Create new OAuth App'));
92+
93+
expect(openExternalMock).toHaveBeenCalledTimes(0);
94+
});
95+
96+
it('should open in browser if hostname configured', async () => {
97+
render(
98+
<AppContext.Provider value={{ accounts: mockAccounts }}>
99+
<MemoryRouter>
100+
<LoginEnterpriseRoute />
101+
</MemoryRouter>
102+
</AppContext.Provider>,
103+
);
104+
105+
fireEvent.change(screen.getByLabelText('Hostname'), {
106+
target: { value: 'company.github.com' },
107+
});
108+
109+
fireEvent.click(screen.getByText('Create new OAuth App'));
110+
111+
expect(openExternalMock).toHaveBeenCalledTimes(1);
112+
});
113+
});
114+
78115
it('should receive a logged-in enterprise account', () => {
79116
const { rerender } = render(
80117
<AppContext.Provider value={{ accounts: mockAccounts }}>

src/routes/LoginEnterprise.tsx

+26-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { useNavigate } from 'react-router-dom';
99
import { FieldInput } from '../components/fields/FieldInput';
1010
import { AppContext } from '../context/App';
1111
import type { AuthOptions } from '../types';
12+
import { getNewOAuthAppURL } from '../utils/auth';
13+
import { openExternalLink } from '../utils/comms';
1214

1315
interface IValues {
1416
hostname?: string;
@@ -65,15 +67,38 @@ export const LoginEnterpriseRoute: FC = () => {
6567
}
6668
}, [enterpriseAccounts]);
6769

70+
const openLink = useCallback((url: string) => {
71+
openExternalLink(url);
72+
}, []);
73+
6874
const renderForm = (formProps: FormRenderProps) => {
69-
const { handleSubmit, submitting, pristine } = formProps;
75+
const { handleSubmit, submitting, pristine, values } = formProps;
76+
77+
const buttonClasses =
78+
'rounded bg-gray-300 font-semibold rounded text-sm text-center hover:bg-gray-500 hover:text-white dark:text-black focus:outline-none cursor-pointer';
7079

7180
return (
7281
<form onSubmit={handleSubmit}>
7382
<FieldInput
7483
name="hostname"
7584
label="Hostname"
7685
placeholder="github.company.com"
86+
helpText={
87+
<div>
88+
<div className="mb-1">
89+
<button
90+
type="button"
91+
className={`px-2 py-1 text-xs ${buttonClasses}`}
92+
disabled={!values.hostname}
93+
onClick={() => openLink(getNewOAuthAppURL(values.hostname))}
94+
>
95+
Create new OAuth App
96+
</button>{' '}
97+
then{' '}
98+
<span className="italic">generate a new client secret</span>.
99+
</div>
100+
</div>
101+
}
77102
/>
78103

79104
<FieldInput name="clientId" label="Client ID" placeholder="123456789" />

src/routes/__snapshots__/LoginEnterprise.test.tsx.snap

+27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/utils/auth.test.ts

+19
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const browserWindow = new remote.BrowserWindow();
66
import type { AuthState } from '../types';
77
import * as apiRequests from './api/request';
88
import * as auth from './auth';
9+
import { getNewOAuthAppURL } from './auth';
910

1011
describe('utils/auth.tsx', () => {
1112
describe('authGitHub', () => {
@@ -130,4 +131,22 @@ describe('utils/auth.tsx', () => {
130131
});
131132
});
132133
});
134+
135+
describe('getNewOAuthAppURL', () => {
136+
it('should generate new oauth app url - github cloud', () => {
137+
expect(
138+
getNewOAuthAppURL('github.com').startsWith(
139+
'https://github.com/settings/applications/new',
140+
),
141+
).toBeTruthy();
142+
});
143+
144+
it('should generate new oauth app url - github server', () => {
145+
expect(
146+
getNewOAuthAppURL('github.gitify.io').startsWith(
147+
'https://github.gitify.io/settings/applications/new',
148+
),
149+
).toBeTruthy();
150+
});
151+
});
133152
});

src/utils/auth.ts

+22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BrowserWindow } from '@electron/remote';
22

3+
import { format } from 'date-fns';
34
import type {
45
AuthResponse,
56
AuthState,
@@ -136,3 +137,24 @@ export const addAccount = (
136137
],
137138
};
138139
};
140+
141+
export function getNewOAuthAppURL(hostname: string): string {
142+
const date = format(new Date(), 'PP p');
143+
const newOAuthAppURL = new URL(
144+
`https://${hostname}/settings/applications/new`,
145+
);
146+
newOAuthAppURL.searchParams.append(
147+
'oauth_application[name]',
148+
`Gitify (Created on ${date})`,
149+
);
150+
newOAuthAppURL.searchParams.append(
151+
'oauth_application[url]',
152+
'https://www.gitify.io',
153+
);
154+
newOAuthAppURL.searchParams.append(
155+
'oauth_application[callback_url]',
156+
'https://www.gitify.io/callback',
157+
);
158+
159+
return newOAuthAppURL.toString();
160+
}

0 commit comments

Comments
 (0)