Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
fc7fe73
fix: optimize_issues_field
Utkarsh-0304 Feb 4, 2026
ae0edf8
fix: use filter() inplace of get() to remove redundant prefetch
Utkarsh-0304 Feb 4, 2026
59515ad
Merge branch 'main' into fix/optimize_issues_field
Utkarsh-0304 Feb 5, 2026
fd3f7fe
fix: use Prefetch within repository query and revert filter() to get()
Utkarsh-0304 Feb 7, 2026
6bb6136
Merge branch 'main' of https://github.com/OWASP/Nest into fix/optimiz…
Utkarsh-0304 Feb 7, 2026
d9ad6ac
Merge branch 'fix/optimize_issues_field' of https://github.com/Utkars…
Utkarsh-0304 Feb 7, 2026
04486aa
Merge branch 'main' of https://github.com/OWASP/Nest into fix/optimiz…
Utkarsh-0304 Feb 9, 2026
fc6b584
Merge branch 'main' of https://github.com/OWASP/Nest into fix/optimiz…
Utkarsh-0304 Feb 10, 2026
cbc7622
fix: prefetch nested queries in RepositoryNode
Utkarsh-0304 Feb 10, 2026
a2664a2
Merge branch 'main' of https://github.com/OWASP/Nest into fix/optimiz…
Utkarsh-0304 Feb 10, 2026
fa0d36b
fix: coderabbit
Utkarsh-0304 Feb 10, 2026
0df68d7
Merge branch 'main' into fix/optimize_issues_field
Utkarsh-0304 Feb 12, 2026
0da5010
Merge branch 'main' into fix/optimize_issues_field
ahmedxgouda Feb 12, 2026
093e76b
Update code
ahmedxgouda Feb 12, 2026
1155890
fix: remove N + 1 for author field, resolve cubic comments and modify…
Utkarsh-0304 Feb 12, 2026
abb8e24
Merge branch 'main' into fix/optimize_issues_field
ahmedxgouda Feb 14, 2026
8ac09e6
Update code
ahmedxgouda Feb 14, 2026
ff0f705
Update tests
ahmedxgouda Feb 14, 2026
ab44995
Merge branch 'main' of https://github.com/OWASP/Nest into fix/optimiz…
Utkarsh-0304 Feb 16, 2026
21a1f43
fix: improve code coverage
Utkarsh-0304 Feb 16, 2026
edf23c3
fix: cubic-ai
Utkarsh-0304 Feb 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions backend/apps/github/api/internal/nodes/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from apps.github.api.internal.nodes.pull_request import PullRequestNode
from apps.github.api.internal.nodes.user import UserNode
from apps.github.models.issue import Issue
from apps.github.models.label import Label
from apps.github.models.pull_request import PullRequest
from apps.mentorship.models.issue_user_interest import IssueUserInterest

Expand All @@ -19,6 +20,12 @@
to_attr="merged_pull_requests",
)

LABELS_PREFETCH = Prefetch(
"labels",
queryset=Label.objects.all(),
to_attr="label_names",
)


@strawberry_django.type(
Issue,
Expand Down Expand Up @@ -52,10 +59,10 @@ def repository_name(self, root: Issue) -> str | None:
"""Resolve the repository name."""
return root.repository.name if root.repository else None

@strawberry_django.field(prefetch_related=["labels"])
@strawberry_django.field(prefetch_related=[LABELS_PREFETCH])
def labels(self, root: Issue) -> list[str]:
"""Resolve label names for the issue."""
return [label.name for label in root.labels.all()]
return [label.name for label in getattr(root, "label_names", [])]

@strawberry_django.field(prefetch_related=[MERGED_PULL_REQUESTS_PREFETCH])
def is_merged(self, root: Issue) -> bool:
Expand Down
20 changes: 18 additions & 2 deletions backend/apps/github/api/internal/nodes/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

import strawberry
import strawberry_django
from django.db.models import Prefetch

from apps.common.utils import normalize_limit
from apps.github.api.internal.nodes.issue import IssueNode
from apps.github.api.internal.nodes.issue import LABELS_PREFETCH, IssueNode
from apps.github.api.internal.nodes.milestone import MilestoneNode
from apps.github.api.internal.nodes.organization import OrganizationNode
from apps.github.api.internal.nodes.release import ReleaseNode
from apps.github.api.internal.nodes.repository_contributor import RepositoryContributorNode
from apps.github.models.issue import Issue
from apps.github.models.repository import Repository

if TYPE_CHECKING:
Expand Down Expand Up @@ -45,10 +47,24 @@ class RepositoryNode(strawberry.relay.Node):

organization: OrganizationNode | None = strawberry_django.field()

@strawberry_django.field(prefetch_related=["issues"])
@strawberry_django.field(
prefetch_related=[
Prefetch(
"issues",
queryset=Issue.objects.select_related(
"author__owasp_profile", "repository__organization"
)
.prefetch_related(LABELS_PREFETCH)
.order_by("-created_at")[:RECENT_ISSUES_LIMIT],
to_attr="recent_issues",
)
]
)
def issues(self, root: Repository) -> list[IssueNode]:
"""Resolve recent issues."""
# TODO(arkid15r): rename this to recent_issues.
if hasattr(root, "recent_issues"):
return root.recent_issues
return root.issues.order_by("-created_at")[:RECENT_ISSUES_LIMIT]

@strawberry_django.field
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_labels(self):
mock_label1.name = "bug"
mock_label2 = Mock()
mock_label2.name = "enhancement"
mock_issue.labels.all.return_value = [mock_label1, mock_label2]
mock_issue.label_names = [mock_label1, mock_label2]

field = self._get_field_by_name("labels", IssueNode)
result = field.base_resolver.wrapped_func(None, mock_issue)
Expand Down
29 changes: 24 additions & 5 deletions backend/tests/apps/github/api/internal/nodes/repository_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from apps.github.api.internal.nodes.milestone import MilestoneNode
from apps.github.api.internal.nodes.organization import OrganizationNode
from apps.github.api.internal.nodes.release import ReleaseNode
from apps.github.api.internal.nodes.repository import RepositoryNode
from apps.github.api.internal.nodes.repository import RECENT_ISSUES_LIMIT, RepositoryNode
from apps.github.api.internal.nodes.repository_contributor import RepositoryContributorNode
from tests.apps.common.graphql_node_base_test import GraphQLNodeBaseTest

Expand Down Expand Up @@ -91,16 +91,35 @@ def test_resolve_url(self):
assert field is not None
assert field.type is str

def test_issues_method(self):
"""Test issues method resolution."""
def test_issues_method_with_recent_issues_attribute(self):
"""Test issues method when recent_issues attribute is available."""
mock_repository = Mock()
mock_issues = []
mock_repository.recent_issues = mock_issues

field = self._get_field_by_name("issues", RepositoryNode)
result = field.base_resolver.wrapped_func(None, mock_repository)
assert result == mock_issues

def test_issues_method_without_recent_issues_attribute(self):
"""Test issues method when recent_issues doesn't exist."""
mock_repository = Mock(spec=["issues"])
mock_issues = Mock()
mock_issues.order_by.return_value.__getitem__ = Mock(return_value=[])
mock_ordered_queryset = Mock()
mock_ordered_queryset.__getitem__ = Mock(return_value=[])

mock_issues.order_by.return_value = mock_ordered_queryset
mock_repository.issues = mock_issues

field = self._get_field_by_name("issues", RepositoryNode)
field.base_resolver.wrapped_func(None, mock_repository)
resolver = field.base_resolver.wrapped_func
result = resolver(None, mock_repository)

mock_issues.order_by.assert_called_with("-created_at")
mock_ordered_queryset.__getitem__.assert_called_with(
slice(None, RECENT_ISSUES_LIMIT, None)
)
assert result == []

def test_recent_milestones_with_invalid_limit(self):
"""Test recent_milestones returns empty list for invalid limit."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ def test_resolve_repository_not_found(self):
mock_queryset = MagicMock()
mock_queryset.select_related.return_value = mock_queryset
mock_queryset.get.side_effect = Repository.DoesNotExist

with patch(
"apps.github.models.repository.Repository.objects",
mock_queryset,
Expand Down