Skip to content

Commit 5ce2624

Browse files
committed
Add GitCommitterIdentityHandler and change identity check
User should be asked for name and email only when none of the following conditions are satisfied - * git config contains user.name and user.email * GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL are set * EMAIL is set If any of the above is true, git doesn't show warning to set the author info.
1 parent 99849b7 commit 5ce2624

File tree

5 files changed

+149
-94
lines changed

5 files changed

+149
-94
lines changed

jupyterlab_git/handlers.py

+18
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,23 @@ async def post(self):
514514
self.finish(json.dumps(response))
515515

516516

517+
class GitCommitterIdentityHandler(GitHandler):
518+
"""
519+
Handler for checking if user identity is set
520+
"""
521+
522+
@web.authenticated
523+
async def get(self):
524+
identity_established = False
525+
top_repo_path = self.get_argument("path")
526+
response = await self.git.config(top_repo_path)
527+
if "user.name" in response["options"] and "user.email" in response["options"]:
528+
identity_established = True
529+
if "GIT_COMMITTER_NAME" in os.environ and "GIT_COMMITTER_EMAIL" in os.environ or "EMAIL" in os.environ:
530+
identity_established = True
531+
self.finish(json.dumps({"identityEstablished": identity_established}))
532+
533+
517534
class GitDiffContentHandler(GitHandler):
518535
"""
519536
Handler for plain text diffs. Uses git show $REF:$FILE
@@ -576,6 +593,7 @@ def setup_handlers(web_app):
576593
("/git/show_top_level", GitShowTopLevelHandler),
577594
("/git/status", GitStatusHandler),
578595
("/git/upstream", GitUpstreamHandler),
596+
("/git/committer_identity", GitCommitterIdentityHandler),
579597
]
580598

581599
# add the baseurl to our paths

jupyterlab_git/tests/test_handlers.py

+66
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
GitLogHandler,
1212
GitPushHandler,
1313
GitUpstreamHandler,
14+
GitCommitterIdentityHandler,
1415
setup_handlers,
1516
)
1617

@@ -62,6 +63,71 @@ def test_all_history_handler_localbranch(self, mock_git):
6263
}
6364

6465

66+
class TestCommitterIdentityHandler(ServerTest):
67+
@patch("jupyterlab_git.handlers.GitCommitterIdentityHandler.git", spec=Git)
68+
def test_name_and_email_from_config(self, mock_git):
69+
config_options = {"code": 0, "options": {"user.name": "foo", "user.email": "bar"}}
70+
mock_git.config.return_value = maybe_future(config_options)
71+
72+
response = self.tester.get(["/committer_identity?path=/path/to/repo"])
73+
payload = response.json()
74+
assert payload == {"identityEstablished": True}
75+
76+
@patch("jupyterlab_git.handlers.GitCommitterIdentityHandler.git", spec=Git)
77+
def test_name_missing_from_config(self, mock_git):
78+
config_options = {"code": 0, "options": {"user.email": "bar"}}
79+
mock_git.config.return_value = maybe_future(config_options)
80+
81+
response = self.tester.get(["/committer_identity?path=/path/to/repo"])
82+
payload = response.json()
83+
assert payload == {"identityEstablished": False}
84+
85+
@patch("jupyterlab_git.handlers.GitCommitterIdentityHandler.git", spec=Git)
86+
def test_email_missing_from_config(self, mock_git):
87+
config_options = {"code": 0, "options": {"user.name": "bar"}}
88+
mock_git.config.return_value = maybe_future(config_options)
89+
90+
response = self.tester.get(["/committer_identity?path=/path/to/repo"])
91+
payload = response.json()
92+
assert payload == {"identityEstablished": False}
93+
94+
@patch("jupyterlab_git.handlers.GitCommitterIdentityHandler.git", spec=Git)
95+
@patch.dict(os.environ, {"GIT_COMMITTER_NAME": "foo", "GIT_COMMITTER_EMAIL": "[email protected]"})
96+
def test_committer_name_email_var(self, mock_git):
97+
mock_git.config.return_value = maybe_future({"code": 0, "options": {}})
98+
99+
response = self.tester.get(["/committer_identity?path=/path/to/repo"])
100+
payload = response.json()
101+
assert payload == {"identityEstablished": True}
102+
103+
@patch("jupyterlab_git.handlers.GitCommitterIdentityHandler.git", spec=Git)
104+
@patch.dict(os.environ, {"GIT_COMMITTER_NAME": "foo"})
105+
def test_missing_committer_email_var(self, mock_git):
106+
mock_git.config.return_value = maybe_future({"code": 0, "options": {}})
107+
108+
response = self.tester.get(["/committer_identity?path=/path/to/repo"])
109+
payload = response.json()
110+
assert payload == {"identityEstablished": False}
111+
112+
@patch("jupyterlab_git.handlers.GitCommitterIdentityHandler.git", spec=Git)
113+
@patch.dict(os.environ, {"GIT_COMMITTER_EMAIL": "[email protected]"})
114+
def test_committer_name_var(self, mock_git):
115+
mock_git.config.return_value = maybe_future({"code": 0, "options": {}})
116+
117+
response = self.tester.get(["/committer_identity?path=/path/to/repo"])
118+
payload = response.json()
119+
assert payload == {"identityEstablished": False}
120+
121+
@patch("jupyterlab_git.handlers.GitCommitterIdentityHandler.git", spec=Git)
122+
@patch.dict(os.environ, {"EMAIL": "[email protected]"})
123+
def test_committer_name_email_var(self, mock_git):
124+
mock_git.config.return_value = maybe_future({"code": 0, "options": {}})
125+
126+
response = self.tester.get(["/committer_identity?path=/path/to/repo"])
127+
payload = response.json()
128+
assert payload == {"identityEstablished": True}
129+
130+
65131
class TestBranch(ServerTest):
66132
@patch("jupyterlab_git.handlers.GitBranchHandler.git", spec=Git)
67133
def test_branch_handler_localbranch(self, mock_git):

src/components/GitPanel.tsx

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { PathExt } from '@jupyterlab/coreutils';
33
import { FileBrowserModel } from '@jupyterlab/filebrowser';
44
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
55
import { ISettingRegistry } from '@jupyterlab/settingregistry';
6-
import { JSONObject } from '@lumino/coreutils';
76
import Tab from '@material-ui/core/Tab';
87
import Tabs from '@material-ui/core/Tabs';
98
import * as React from 'react';
@@ -383,13 +382,13 @@ export class GitPanel extends React.Component<
383382
// If the repository path changes, check the user identity
384383
if (path !== this._previousRepoPath) {
385384
try {
386-
let res = await this.props.model.config();
385+
let res = await this.props.model.isCommitterIdentitySet();
387386
if (res.ok) {
388-
const options: JSONObject = (await res.json()).options;
389-
const keys = Object.keys(options);
387+
const identityEstablished: boolean = (await res.json())
388+
.identityEstablished;
390389

391390
// If the user name or e-mail is unknown, ask the user to set it
392-
if (keys.indexOf('user.name') < 0 || keys.indexOf('user.email') < 0) {
391+
if (!identityEstablished) {
393392
const result = await showDialog({
394393
title: 'Who is committing?',
395394
body: new GitAuthorForm()

src/model.ts

+33
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,39 @@ export class GitExtension implements IGitExtension {
571571
}
572572
}
573573

574+
async isCommitterIdentitySet(): Promise<Response> {
575+
await this.ready;
576+
const path = this.pathRepository;
577+
578+
if (path === null) {
579+
return Promise.resolve(
580+
new Response(
581+
JSON.stringify({
582+
code: -1,
583+
message: 'Not in a git repository.'
584+
})
585+
)
586+
);
587+
}
588+
589+
try {
590+
const response = await httpGitRequest(
591+
`/git/committer_identity?path=${path}`,
592+
'GET',
593+
null
594+
);
595+
596+
if (!response.ok) {
597+
const jsonData = await response.json();
598+
throw new ServerConnection.ResponseError(response, jsonData.message);
599+
}
600+
601+
return response;
602+
} catch (err) {
603+
throw new ServerConnection.NetworkError(err);
604+
}
605+
}
606+
574607
/**
575608
* Make request to revert changes from selected commit
576609
*

tests/test-components/GitPanel.spec.tsx

+28-89
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,11 @@ describe('GitPanel', () => {
108108

109109
// Mock identity look up
110110
const identity = jest
111-
.spyOn(GitModel.prototype, 'config')
111+
.spyOn(GitModel.prototype, 'isCommitterIdentitySet')
112112
.mockResolvedValue(
113113
new Response(
114114
JSON.stringify({
115-
options: {
116-
'user.name': 'John Snow',
117-
'user.email': '[email protected]'
118-
}
115+
identityEstablished: true
119116
}),
120117
{ status: 201 }
121118
)
@@ -136,81 +133,29 @@ describe('GitPanel', () => {
136133
spy.mockRestore();
137134
});
138135

139-
it('should prompt for user identity if user.name is not set', async () => {
136+
it('should prompt for user identity if not set', async () => {
140137
const spy = jest.spyOn(GitModel.prototype, 'commit');
141138

142-
// Mock identity look up
143-
const identity = jest
139+
const config = jest
144140
.spyOn(GitModel.prototype, 'config')
145141
.mockImplementation(options => {
146142
let response: Response = null;
147-
if (options === undefined) {
148-
response = new Response(
149-
JSON.stringify({
150-
options: {
151-
'user.email': '[email protected]'
152-
}
153-
}),
154-
{ status: 201 }
155-
);
156-
} else {
157-
response = new Response('', { status: 201 });
158-
}
143+
response = new Response('', { status: 201 });
159144
return Promise.resolve(response);
160145
});
161-
const mock = apputils as jest.Mocked<typeof apputils>;
162-
mock.showDialog.mockResolvedValue({
163-
button: {
164-
accept: true,
165-
caption: '',
166-
className: '',
167-
displayType: 'default',
168-
iconClass: '',
169-
iconLabel: '',
170-
label: ''
171-
},
172-
value: {
173-
name: 'John Snow',
174-
175-
}
176-
});
177-
178-
const panel = new GitPanel(props);
179-
await panel.commitStagedFiles('Initial commit');
180-
expect(identity).toHaveBeenCalledTimes(2);
181-
expect(identity.mock.calls[0]).toHaveLength(0);
182-
expect(identity.mock.calls[1]).toEqual([
183-
{
184-
'user.name': 'John Snow',
185-
'user.email': '[email protected]'
186-
}
187-
]);
188-
expect(spy).toHaveBeenCalledTimes(1);
189-
expect(spy).toHaveBeenCalledWith('Initial commit');
190-
});
191-
192-
it('should prompt for user identity if user.email is not set', async () => {
193-
const spy = jest.spyOn(GitModel.prototype, 'commit');
194146

195147
// Mock identity look up
196148
const identity = jest
197-
.spyOn(GitModel.prototype, 'config')
198-
.mockImplementation(options => {
199-
let response: Response = null;
200-
if (options === undefined) {
201-
response = new Response(
202-
JSON.stringify({
203-
options: {
204-
'user.name': 'John Snow'
205-
}
206-
}),
207-
{ status: 201 }
208-
);
209-
} else {
210-
response = new Response('', { status: 201 });
211-
}
212-
return Promise.resolve(response);
213-
});
149+
.spyOn(GitModel.prototype, 'isCommitterIdentitySet')
150+
.mockResolvedValue(
151+
new Response(
152+
JSON.stringify({
153+
identityEstablished: false
154+
}),
155+
{ status: 201 }
156+
)
157+
);
158+
214159
const mock = apputils as jest.Mocked<typeof apputils>;
215160
mock.showDialog.mockResolvedValue({
216161
button: {
@@ -230,9 +175,9 @@ describe('GitPanel', () => {
230175

231176
const panel = new GitPanel(props);
232177
await panel.commitStagedFiles('Initial commit');
233-
expect(identity).toHaveBeenCalledTimes(2);
234-
expect(identity.mock.calls[0]).toHaveLength(0);
235-
expect(identity.mock.calls[1]).toEqual([
178+
expect(identity).toHaveBeenCalledTimes(1);
179+
expect(config).toHaveBeenCalledTimes(1);
180+
expect(config.mock.calls[0]).toEqual([
236181
{
237182
'user.name': 'John Snow',
238183
'user.email': '[email protected]'
@@ -247,21 +192,16 @@ describe('GitPanel', () => {
247192

248193
// Mock identity look up
249194
const identity = jest
250-
.spyOn(GitModel.prototype, 'config')
251-
.mockImplementation(options => {
252-
let response: Response = null;
253-
if (options === undefined) {
254-
response = new Response(
255-
JSON.stringify({
256-
options: {}
257-
}),
258-
{ status: 201 }
259-
);
260-
} else {
261-
response = new Response('', { status: 201 });
262-
}
263-
return Promise.resolve(response);
264-
});
195+
.spyOn(GitModel.prototype, 'isCommitterIdentitySet')
196+
.mockResolvedValue(
197+
new Response(
198+
JSON.stringify({
199+
identityEstablished: false
200+
}),
201+
{ status: 201 }
202+
)
203+
);
204+
265205
const mock = apputils as jest.Mocked<typeof apputils>;
266206
mock.showDialog.mockResolvedValue({
267207
button: {
@@ -279,7 +219,6 @@ describe('GitPanel', () => {
279219
const panel = new GitPanel(props);
280220
await panel.commitStagedFiles('Initial commit');
281221
expect(identity).toHaveBeenCalledTimes(1);
282-
expect(identity).toHaveBeenCalledWith();
283222
expect(spy).not.toHaveBeenCalled();
284223
});
285224
});

0 commit comments

Comments
 (0)