Skip to content

Commit 96aaf2e

Browse files
authored
Add method 'get_repository' (#2)
This replaces getsentry/sentry#112569 and getsentry/seer#5688.
1 parent fec03dd commit 96aaf2e

File tree

8 files changed

+137
-0
lines changed

8 files changed

+137
-0
lines changed

src/scm/actions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@
5454
GetPullRequestProtocol,
5555
GetPullRequestReactionsProtocol,
5656
GetPullRequestsProtocol,
57+
GetRepositoryProtocol,
5758
GetTreeProtocol,
5859
GitBlob,
5960
GitCommitObject,
6061
GitRef,
62+
GitRepository,
6163
GitTree,
6264
InputTreeEntry,
6365
MinimizeCommentProtocol,
@@ -83,6 +85,11 @@
8385
)
8486

8587

88+
def get_repository(scm: GetRepositoryProtocol) -> ActionResult[GitRepository]:
89+
"""Get the repository associated with this SourceCodeManager."""
90+
return scm.get_repository()
91+
92+
8693
def get_issue_comments(
8794
scm: GetIssueCommentsProtocol,
8895
issue_id: str,

src/scm/providers/github/provider.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
GitCommitObject,
3636
GitCommitTree,
3737
GitRef,
38+
GitRepository,
3839
GitTree,
3940
InputTreeEntry,
4041
PaginatedActionResult,
@@ -302,6 +303,10 @@ def graphql(
302303

303304
return response_data.get("data", {})
304305

306+
def get_repository(self) -> ActionResult[GitRepository]:
307+
response = self.get(f"/repos/{self.repository['name']}")
308+
return map_action(response, map_repository)
309+
305310
def get_issue_comments(
306311
self,
307312
issue_id: str,
@@ -1064,6 +1069,16 @@ def map_pull_request(raw: dict[str, Any]) -> PullRequest:
10641069
)
10651070

10661071

1072+
def map_repository(raw: dict[str, Any]) -> GitRepository:
1073+
return GitRepository(
1074+
full_name=raw["full_name"],
1075+
default_branch=raw["default_branch"],
1076+
clone_url=raw["clone_url"],
1077+
private=raw["private"],
1078+
size=raw["size"],
1079+
)
1080+
1081+
10671082
def map_action[T](response: requests.Response, fn: Callable[[dict[str, Any]], T]) -> ActionResult[T]:
10681083
raw = response.json()
10691084
return {

src/scm/providers/gitlab/provider.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
GitCommitObject,
2121
GitCommitTree,
2222
GitRef,
23+
GitRepository,
2324
GitTree,
2425
PaginatedActionResult,
2526
PaginatedResponseMeta,
@@ -222,6 +223,10 @@ def patch(
222223
def delete(self, path: str) -> requests.Response:
223224
return self._request("DELETE", path=path)
224225

226+
def get_repository(self) -> ActionResult[GitRepository]:
227+
response = self.get(GitLab.project.format(project=self.project_id), params={"statistics": "true"})
228+
return make_result(map_repository, response.json())
229+
225230
def get_issue_comments(
226231
self,
227232
issue_id: str,
@@ -874,6 +879,19 @@ def map_pull_request_file(raw: dict[str, Any]) -> PullRequestFile:
874879
)
875880

876881

882+
def map_repository(raw: dict[str, Any]) -> GitRepository:
883+
statistics = raw.get("statistics")
884+
repo_size = statistics.get("repository_size", 0) if statistics else 0
885+
return GitRepository(
886+
full_name=raw["path_with_namespace"],
887+
default_branch=raw.get("default_branch"),
888+
clone_url=raw["http_url_to_repo"],
889+
private=raw["visibility"] != "public",
890+
# GitLab returns size in bytes. We convert to kB to match GitHub
891+
size=repo_size // 1000,
892+
)
893+
894+
877895
def map_reaction_result(raw: dict[str, Any]) -> ReactionResult:
878896
return ReactionResult(
879897
id=str(raw["id"]),

src/scm/types.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,16 @@ class Repository(TypedDict):
264264
provider_name: ProviderName
265265

266266

267+
class GitRepository(TypedDict):
268+
"""Information about a git repository at an SCM provider."""
269+
270+
full_name: str
271+
default_branch: str | None
272+
clone_url: str
273+
private: bool
274+
size: int
275+
276+
267277
class GitRef(TypedDict):
268278
"""A git reference (branch pointer)."""
269279

@@ -460,6 +470,14 @@ class PullRequestEventData(TypedDict):
460470
author: Author | None
461471

462472

473+
# Basic protocols
474+
475+
476+
@runtime_checkable
477+
class GetRepositoryProtocol(Protocol):
478+
def get_repository(self) -> ActionResult[GitRepository]: ...
479+
480+
463481
# Issue Comment Protocols
464482

465483

@@ -986,6 +1004,7 @@ def resolve_review_thread(self, thread_node_id: str) -> None: ...
9861004
GetPullRequestProtocol,
9871005
GetPullRequestReactionsProtocol,
9881006
GetPullRequestsProtocol,
1007+
GetRepositoryProtocol,
9891008
GetTreeProtocol,
9901009
MinimizeCommentProtocol,
9911010
RequestReviewProtocol,

tests/test_fixtures.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@
4444
)
4545

4646

47+
def make_github_repository() -> dict[str, Any]:
48+
"""Factory for GitHub repository API responses."""
49+
return {
50+
"full_name": "test-org/test-repo",
51+
"default_branch": "main",
52+
"clone_url": "https://github.com/test-org/test-repo.git",
53+
"private": False,
54+
"size": 1234,
55+
}
56+
57+
4758
def make_github_comment(
4859
comment_id: int = 1,
4960
body: str = "Test comment",

tests/unit/provider/test_github.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
make_github_pull_request_commit,
2727
make_github_pull_request_file,
2828
make_github_reaction,
29+
make_github_repository,
2930
make_github_review,
3031
make_github_review_comment,
3132
)
@@ -182,6 +183,16 @@ def make_provider(client: RecordingClient | None = None) -> tuple[GitHubProvider
182183
return provider, transport
183184

184185

186+
def expected_repository(raw: dict[str, Any]) -> dict[str, Any]:
187+
return {
188+
"full_name": raw["full_name"],
189+
"default_branch": raw["default_branch"],
190+
"clone_url": raw["clone_url"],
191+
"private": raw["private"],
192+
"size": raw["size"],
193+
}
194+
195+
185196
def expected_comment(raw: dict[str, Any]) -> dict[str, Any]:
186197
return {
187198
"id": str(raw["id"]),
@@ -331,6 +342,7 @@ def expected_check_run(raw: dict[str, Any]) -> dict[str, Any]:
331342
}
332343

333344

345+
REPOSITORY_RAW = make_github_repository()
334346
COMMENT_RAW = make_github_comment()
335347
REACTION_RAW = make_github_reaction()
336348
PULL_REQUEST_RAW = make_github_pull_request()
@@ -459,6 +471,14 @@ def expected_check_run(raw: dict[str, Any]) -> dict[str, Any]:
459471

460472
ACTION_CASES: list[dict[str, Any]] = [
461473
{
474+
"name": "get_repository",
475+
"operation": "get",
476+
"kwargs": {},
477+
"path": "/repos/test-org/test-repo",
478+
"raw": REPOSITORY_RAW,
479+
"expected_data": expected_repository(REPOSITORY_RAW),
480+
},
481+
{
462482
"name": "create_issue_comment",
463483
"operation": "post",
464484
"kwargs": {"issue_id": "42", "body": "hello"},

tests/unit/provider/test_gitlab.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,45 @@ def _make_mock_response(json_data):
5656
@pytest.mark.parametrize(
5757
"param",
5858
[
59+
ForwardToClientTest(
60+
provider_method=GitLabProvider.get_repository,
61+
provider_args={},
62+
client_calls=[
63+
ClientForwardedCall(
64+
method="GET",
65+
path="/projects/79787061",
66+
params={"statistics": "true"},
67+
json_response={
68+
"path_with_namespace": "test-org/test-repo",
69+
"default_branch": "main",
70+
"http_url_to_repo": "https://gitlab.com/test-org/test-repo.git",
71+
"visibility": "public",
72+
"statistics": {"repository_size": 123456},
73+
},
74+
),
75+
],
76+
provider_return_value={
77+
"data": {
78+
"full_name": "test-org/test-repo",
79+
"default_branch": "main",
80+
"clone_url": "https://gitlab.com/test-org/test-repo.git",
81+
"private": False,
82+
"size": 123, # Size converted from bytes to kB
83+
},
84+
"type": "gitlab",
85+
"raw": {
86+
"data": {
87+
"path_with_namespace": "test-org/test-repo",
88+
"default_branch": "main",
89+
"http_url_to_repo": "https://gitlab.com/test-org/test-repo.git",
90+
"visibility": "public",
91+
"statistics": {"repository_size": 123456},
92+
},
93+
"headers": None,
94+
},
95+
"meta": {},
96+
},
97+
),
5998
# Actual calls made with an actual GitLab integration, captured for this file.
6099
ForwardToClientTest(
61100
provider_method=GitLabProvider.get_pull_requests,

tests/unit/test_rpc_integration.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
make_github_pull_request_commit,
3131
make_github_pull_request_file,
3232
make_github_reaction,
33+
make_github_repository,
3334
make_github_review,
3435
make_github_review_comment,
3536
)
@@ -128,6 +129,13 @@ def make_client_scm(organization_id, repository_id, server):
128129
# Each entry: (action_name, action_callable, mock_response_body, status_code, extra_headers)
129130
# action_callable receives the scm instance and calls the action with appropriate args.
130131
ACTION_TEST_CASES: list[tuple[str, Callable, dict | list | str, int, dict[str, str] | None]] = [
132+
(
133+
"get_repository",
134+
lambda scm: actions.get_repository(scm),
135+
make_github_repository(),
136+
200,
137+
None,
138+
),
131139
# Pull request operations
132140
(
133141
"get_pull_request",

0 commit comments

Comments
 (0)