Skip to content

Commit 1492ce8

Browse files
Moved /repositories under organization (OWASP#1380)
* moved /repositories under organization * case insensitive URL * Update code * recent issues/releases/PR handled * Update code --------- Co-authored-by: Arkadii Yakovets <[email protected]>
1 parent fb4c6a2 commit 1492ce8

File tree

28 files changed

+148
-39
lines changed

28 files changed

+148
-39
lines changed

backend/apps/github/graphql/nodes/issue.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
class IssueNode(BaseNode):
1010
"""GitHub issue node."""
1111

12+
organization_name = graphene.String()
1213
repository_name = graphene.String()
1314

1415
class Meta:
@@ -21,6 +22,10 @@ class Meta:
2122
"url",
2223
)
2324

25+
def resolve_organization_name(self, info):
26+
"""Return organization name."""
27+
return self.repository.organization.login if self.repository.organization else None
28+
2429
def resolve_repository_name(self, info):
2530
"""Resolve the repository name."""
2631
return self.repository.name

backend/apps/github/graphql/nodes/pull_request.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
class PullRequestNode(BaseNode):
1010
"""GitHub pull request node."""
1111

12+
organization_name = graphene.String()
1213
repository_name = graphene.String()
1314
url = graphene.String()
1415

@@ -20,6 +21,10 @@ class Meta:
2021
"title",
2122
)
2223

24+
def resolve_organization_name(self, info):
25+
"""Return organization name."""
26+
return self.repository.organization.login if self.repository.organization else None
27+
2328
def resolve_repository_name(self, info):
2429
"""Resolve repository name."""
2530
return self.repository.name

backend/apps/github/graphql/nodes/release.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class ReleaseNode(BaseNode):
1212
"""GitHub release node."""
1313

1414
author = graphene.Field(UserNode)
15+
organization_name = graphene.String()
1516
project_name = graphene.String()
1617
repository_name = graphene.String()
1718
url = graphene.String()
@@ -26,6 +27,10 @@ class Meta:
2627
"tag_name",
2728
)
2829

30+
def resolve_organization_name(self, info):
31+
"""Return organization name."""
32+
return self.repository.organization.login if self.repository.organization else None
33+
2934
def resolve_project_name(self, info):
3035
"""Return project name."""
3136
return self.repository.project.name.lstrip(OWASP_ORGANIZATION_NAME)

backend/apps/github/graphql/nodes/repository.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Meta:
3636
"license",
3737
"name",
3838
"open_issues_count",
39+
"organization",
3940
"size",
4041
"stars_count",
4142
"subscribers_count",

backend/apps/github/graphql/queries/repository.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class RepositoryQuery(BaseQuery):
1212

1313
repository = graphene.Field(
1414
RepositoryNode,
15+
organization_key=graphene.String(required=True),
1516
repository_key=graphene.String(required=True),
1617
)
1718

@@ -21,20 +22,24 @@ class RepositoryQuery(BaseQuery):
2122
limit=graphene.Int(default_value=12),
2223
)
2324

24-
def resolve_repository(root, info, repository_key):
25+
def resolve_repository(root, info, organization_key, repository_key):
2526
"""Resolve repository by key.
2627
2728
Args:
2829
root (Any): The root query object.
2930
info (ResolveInfo): The GraphQL execution context.
31+
organization_key (str): The login of the organization.
3032
repository_key (str): The unique key of the repository.
3133
3234
Returns:
3335
Repository or None: The repository object if found, otherwise None.
3436
3537
"""
3638
try:
37-
return Repository.objects.get(key=repository_key)
39+
return Repository.objects.select_related("organization").get(
40+
key__iexact=repository_key,
41+
organization__login__iexact=organization_key,
42+
)
3843
except Repository.DoesNotExist:
3944
return None
4045

@@ -56,7 +61,7 @@ def resolve_repositories(root, info, organization, limit):
5661
"organization",
5762
)
5863
.filter(
59-
organization__login=organization,
64+
organization__login__iexact=organization,
6065
)
6166
.order_by("-stars_count")[:limit]
6267
)

backend/tests/apps/github/graphql/nodes/issue_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def test_meta_configuration(self):
1818
expected_fields = {
1919
"author",
2020
"created_at",
21+
"organization_name",
2122
"repository_name",
2223
"state",
2324
"title",

backend/tests/apps/github/graphql/nodes/release_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def test_meta_configuration(self):
2222
"author",
2323
"is_pre_release",
2424
"name",
25+
"organization_name",
2526
"project_name",
2627
"published_at",
2728
"repository_name",

backend/tests/apps/github/graphql/nodes/repository_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def test_meta_configuration(self):
2727
"license",
2828
"name",
2929
"open_issues_count",
30+
"organization",
3031
"owner_key",
3132
"latest_release",
3233
"releases",

backend/tests/apps/github/graphql/queries/repository_test.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Test cases for RepositoryQuery."""
22

3-
from unittest.mock import Mock, patch
3+
from unittest.mock import MagicMock, Mock, patch
44

55
import pytest
66

@@ -23,24 +23,46 @@ def mock_repository(self):
2323

2424
def test_resolve_repository_existing(self, mock_repository, mock_info):
2525
"""Test resolving an existing repository."""
26-
with patch("apps.github.models.repository.Repository.objects.get") as mock_get:
27-
mock_get.return_value = mock_repository
26+
mock_queryset = MagicMock()
27+
mock_queryset.get.return_value = mock_repository
2828

29+
with patch(
30+
"apps.github.models.repository.Repository.objects.select_related",
31+
return_value=mock_queryset,
32+
) as mock_select_related:
2933
result = RepositoryQuery.resolve_repository(
30-
None, mock_info, repository_key="test-repo"
34+
None,
35+
mock_info,
36+
organization_key="test-org",
37+
repository_key="test-repo",
3138
)
3239

3340
assert result == mock_repository
34-
mock_get.assert_called_once_with(key="test-repo")
41+
mock_select_related.assert_called_once_with("organization")
42+
mock_queryset.get.assert_called_once_with(
43+
organization__login__iexact="test-org",
44+
key__iexact="test-repo",
45+
)
3546

3647
def test_resolve_repository_not_found(self, mock_info):
3748
"""Test resolving a non-existent repository."""
38-
with patch("apps.github.models.repository.Repository.objects.get") as mock_get:
39-
mock_get.side_effect = Repository.DoesNotExist
49+
mock_queryset = MagicMock()
50+
mock_queryset.get.side_effect = Repository.DoesNotExist
4051

52+
with patch(
53+
"apps.github.models.repository.Repository.objects.select_related",
54+
return_value=mock_queryset,
55+
) as mock_select_related:
4156
result = RepositoryQuery.resolve_repository(
42-
None, mock_info, repository_key="non-existent-repo"
57+
None,
58+
mock_info,
59+
organization_key="non-existent-org",
60+
repository_key="non-existent-repo",
4361
)
4462

4563
assert result is None
46-
mock_get.assert_called_once_with(key="non-existent-repo")
64+
mock_select_related.assert_called_once_with("organization")
65+
mock_queryset.get.assert_called_once_with(
66+
organization__login__iexact="non-existent-org",
67+
key__iexact="non-existent-repo",
68+
)

frontend/__tests__/e2e/pages/ProjectDetails.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,6 @@ test.describe('Project Details Page', () => {
102102
await expect(page.getByText('Issues3', { exact: true })).toBeVisible()
103103

104104
await page.getByText('Repo One').click()
105-
await expect(page).toHaveURL('repositories/repo-1')
105+
await expect(page).toHaveURL('organizations/OWASP/repositories/repo-1')
106106
})
107107
})

0 commit comments

Comments
 (0)