Skip to content

Commit 9080f0d

Browse files
authored
Limit repo collaborators to outside and direct affiliations. (#7)
* fix: fix collaborators to only return direct and outside contributors * chore: update and bump version * fix: add affiliation to yaml
1 parent 2cfb11b commit 9080f0d

File tree

8 files changed

+126
-36
lines changed

8 files changed

+126
-36
lines changed

nodestream_github/client/githubclient.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -390,9 +390,7 @@ async def fetch_webhooks_for_repo(
390390
_fetch_problem(f"webhooks for repo {owner_login}/{repo_name}", e)
391391

392392
async def fetch_collaborators_for_repo(
393-
self,
394-
owner_login: str,
395-
repo_name: str,
393+
self, owner_login: str, repo_name: str, affiliation: str
396394
) -> AsyncGenerator[types.GithubUser]:
397395
"""Try to get collaborator data for this repo.
398396
@@ -415,7 +413,8 @@ async def fetch_collaborators_for_repo(
415413
"""
416414
try:
417415
async for collab_resp in self._get_paginated(
418-
f"repos/{owner_login}/{repo_name}/collaborators"
416+
f"repos/{owner_login}/{repo_name}/collaborators",
417+
params={"affiliation": affiliation},
419418
):
420419
yield collab_resp
421420

nodestream_github/github_repos.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
key_normalization:
8585
do_lowercase_strings: false
8686
relationship_properties:
87+
"affiliation": !jmespath "affiliation"
8788
"permission": !jmespath "role_name"
8889

8990
- type: relationship

nodestream_github/interpretations/relationship/user.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,19 @@
1313
_USER_KEYS_TO_PRESERVE = ["id", "login", "node_id", "role", "role_name"]
1414

1515

16-
def simplify_user(user: GithubUser) -> SimplifiedUser:
16+
def simplify_user(
17+
user: GithubUser,
18+
*,
19+
affiliation: str | None = None,
20+
) -> SimplifiedUser:
1721
"""Simplify user data.
1822
1923
Allows us to only keep a consistent minimum for relationship data."""
20-
return {k: user[k] for k in _USER_KEYS_TO_PRESERVE if k in user}
24+
output = {k: user[k] for k in _USER_KEYS_TO_PRESERVE if k in user}
25+
26+
if affiliation:
27+
output["affiliation"] = affiliation
28+
return output
2129

2230

2331
class UserRelationshipInterpretation(

nodestream_github/repos.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,21 @@ async def _extract_repo(self, repo: GithubRepo) -> RepositoryRecord:
103103
owner["login"], repo["name"]
104104
)
105105
]
106-
repo["collaborators"] = [
107-
simplify_user(user)
108-
async for user in self.client.fetch_collaborators_for_repo(
109-
owner["login"], repo["name"]
110-
)
111-
]
106+
repo["collaborators"] = []
107+
108+
async for user in self.client.fetch_collaborators_for_repo(
109+
owner["login"],
110+
repo["name"],
111+
"direct",
112+
):
113+
repo["collaborators"].append(simplify_user(user, affiliation="direct"))
114+
async for user in self.client.fetch_collaborators_for_repo(
115+
owner["login"],
116+
repo["name"],
117+
"outside",
118+
):
119+
repo["collaborators"].append(simplify_user(user, affiliation="outside"))
120+
112121
logger.debug("yielded GithubRepo{full_name=%s}", repo["full_name"])
113122
return repo
114123

poetry.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "nodestream-plugin-github"
3-
version = "0.13.1-beta.5"
3+
version = "0.13.1-beta.6"
44
description = ""
55
authors = ["Jon Bristow <[email protected]>"]
66
packages = [

tests/mocks/githubrest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ def get_webhooks_for_repo(self, owner_login: str, repo_name: str, **kwargs: any)
131131
)
132132

133133
def get_collaborators_for_repo(
134-
self, owner_login: str, repo_name: str, **kwargs: any
134+
self, owner_login: str, repo_name: str, affiliation: str, **kwargs: any
135135
) -> None:
136136
self.add_response(
137-
url=f"{self.base_url}/repos/{owner_login}/{repo_name}/collaborators?per_page={self.per_page}",
137+
url=f"{self.base_url}/repos/{owner_login}/{repo_name}/collaborators?per_page={self.per_page}&affiliation={affiliation}",
138138
**kwargs,
139139
)
140140

tests/test_repos.py

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
from nodestream_github import GithubReposExtractor
44
from tests.data.orgs import GITHUB_ORG_SUMMARY
55
from tests.data.repos import HELLO_WORLD_REPO, repo
6-
from tests.data.users import OCTOCAT_USER, TURBO_USER
6+
from tests.data.users import OCTOCAT_USER, TURBO_USER, user
77
from tests.data.webhooks import HELLO_WORLD_WEBHOOK
88
from tests.mocks.githubrest import DEFAULT_HOSTNAME, DEFAULT_PER_PAGE, GithubHttpxMock
99

10+
TEST_USER = user(user_login="bweaver", user_id=3)
11+
1012

1113
@pytest.fixture
1214
def repo_client() -> GithubReposExtractor:
@@ -35,13 +37,30 @@ async def test_pull_org_repos(gh_rest_mock: GithubHttpxMock):
3537
gh_rest_mock.get_repos_for_org("github", "public", json=[HELLO_WORLD_REPO])
3638
gh_rest_mock.get_repos_for_org("github", "private", json=[HELLO_WORLD_REPO])
3739
gh_rest_mock.get_languages_for_repo(
38-
"octocat", "Hello-World", json=[], is_reusable=True
40+
"octocat",
41+
"Hello-World",
42+
json=[],
43+
is_reusable=True,
3944
)
4045
gh_rest_mock.get_webhooks_for_repo(
41-
"octocat", "Hello-World", json=[], is_reusable=True
46+
"octocat",
47+
"Hello-World",
48+
json=[],
49+
is_reusable=True,
50+
)
51+
gh_rest_mock.get_collaborators_for_repo(
52+
"octocat",
53+
"Hello-World",
54+
affiliation="direct",
55+
json=[],
56+
is_reusable=True,
4257
)
4358
gh_rest_mock.get_collaborators_for_repo(
44-
"octocat", "Hello-World", json=[], is_reusable=True
59+
"octocat",
60+
"Hello-World",
61+
affiliation="outside",
62+
json=[],
63+
is_reusable=True,
4564
)
4665

4766
assert len([record async for record in extractor.extract_records()]) == 2
@@ -62,13 +81,30 @@ async def test_pull_user_repos(gh_rest_mock: GithubHttpxMock):
6281
gh_rest_mock.get_repos_for_user("octocat", "public", json=[HELLO_WORLD_REPO])
6382
gh_rest_mock.get_repos_for_user("octocat", "private", json=[HELLO_WORLD_REPO])
6483
gh_rest_mock.get_languages_for_repo(
65-
"octocat", "Hello-World", json=[], is_reusable=True
84+
"octocat",
85+
"Hello-World",
86+
json=[],
87+
is_reusable=True,
6688
)
6789
gh_rest_mock.get_webhooks_for_repo(
68-
"octocat", "Hello-World", json=[], is_reusable=True
90+
"octocat",
91+
"Hello-World",
92+
json=[],
93+
is_reusable=True,
94+
)
95+
gh_rest_mock.get_collaborators_for_repo(
96+
"octocat",
97+
"Hello-World",
98+
affiliation="direct",
99+
json=[],
100+
is_reusable=True,
69101
)
70102
gh_rest_mock.get_collaborators_for_repo(
71-
"octocat", "Hello-World", json=[], is_reusable=True
103+
"octocat",
104+
"Hello-World",
105+
affiliation="outside",
106+
json=[],
107+
is_reusable=True,
72108
)
73109

74110
assert len([record async for record in extractor.extract_records()]) == 2
@@ -94,8 +130,15 @@ async def test_extract_records(
94130
gh_rest_mock.get_collaborators_for_repo(
95131
owner_login="octocat",
96132
repo_name="Hello-World",
133+
affiliation="direct",
97134
json=[TURBO_USER | {"role_name": "write"}],
98135
)
136+
gh_rest_mock.get_collaborators_for_repo(
137+
owner_login="octocat",
138+
repo_name="Hello-World",
139+
affiliation="outside",
140+
json=[TEST_USER | {"role_name": "read"}],
141+
)
99142
gh_rest_mock.get_languages_for_repo(
100143
owner_login="github",
101144
repo_name="Hello-Moon",
@@ -109,8 +152,15 @@ async def test_extract_records(
109152
gh_rest_mock.get_collaborators_for_repo(
110153
owner_login="github",
111154
repo_name="Hello-Moon",
155+
affiliation="direct",
112156
json=[TURBO_USER],
113157
)
158+
gh_rest_mock.get_collaborators_for_repo(
159+
owner_login="github",
160+
repo_name="Hello-Moon",
161+
affiliation="outside",
162+
json=[TEST_USER],
163+
)
114164
assert [record async for record in repo_client.extract_records()] == [
115165
{
116166
"archive_url": (
@@ -125,12 +175,22 @@ async def test_extract_records(
125175
"https://HOSTNAME/repos/octocat/Hello-World/branches{/branch}"
126176
),
127177
"clone_url": "https://github.com/octocat/Hello-World.git",
128-
"collaborators": [{
129-
"id": 2,
130-
"login": "turbo",
131-
"node_id": "MDQ6VXNlcjI=",
132-
"role_name": "write",
133-
}],
178+
"collaborators": [
179+
{
180+
"affiliation": "direct",
181+
"id": 2,
182+
"login": "turbo",
183+
"node_id": "MDQ6VXNlcjI=",
184+
"role_name": "write",
185+
},
186+
{
187+
"affiliation": "outside",
188+
"id": 3,
189+
"login": "bweaver",
190+
"node_id": "MDQ6VXNlcjM=",
191+
"role_name": "read",
192+
},
193+
],
134194
"collaborators_url": "https://HOSTNAME/repos/octocat/Hello-World/collaborators{/collaborator}",
135195
"comments_url": (
136196
"https://HOSTNAME/repos/octocat/Hello-World/comments{/number}"
@@ -280,7 +340,20 @@ async def test_extract_records(
280340
"https://HOSTNAME/repos/github/Hello-Moon/branches{/branch}"
281341
),
282342
"clone_url": "https://github.com/github/Hello-Moon.git",
283-
"collaborators": [{"id": 2, "login": "turbo", "node_id": "MDQ6VXNlcjI="}],
343+
"collaborators": [
344+
{
345+
"affiliation": "direct",
346+
"id": 2,
347+
"login": "turbo",
348+
"node_id": "MDQ6VXNlcjI=",
349+
},
350+
{
351+
"affiliation": "outside",
352+
"id": 3,
353+
"login": "bweaver",
354+
"node_id": "MDQ6VXNlcjM=",
355+
},
356+
],
284357
"collaborators_url": (
285358
"https://HOSTNAME/repos/github/Hello-Moon/collaborators{/collaborator}"
286359
),

0 commit comments

Comments
 (0)