Skip to content

Conversation

@arkid15r
Copy link
Collaborator

@arkid15r arkid15r commented Oct 5, 2025

Proposed change

Extend v0 API endpoints.

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 5, 2025

Summary by CodeRabbit

  • New Features

    • Unified, built-in pagination across all v0 endpoints.
    • Added detail endpoints for events, issues, repositories, and releases.
    • Introduced distinct list vs. detail responses for chapters, committees, events, issues, labels, members, organizations, projects, releases, repositories, and sponsors.
    • Expanded filtering and standardized 404 error payloads.
    • API version bumped to 0.2.4.
  • Improvements

    • Consistent routing, ordering, and response shapes across resources.
  • Performance

    • New database indexes for faster queries on issues, releases, repositories, and events.
  • Tests

    • Updated tests to align with new response schemas.

Walkthrough

Switches v0 API routers to RouterPaginated with a custom pagination class, refactors many endpoint schemas into Base/List/Detail/Error/Filter, adds detail endpoints and 404 mappings, renames model imports to *Model, adds DB indexes and migrations, bumps API version to 0.2.4, and updates tests to new schemas.

Changes

Cohort / File(s) Summary
API bootstrap & pagination
backend/apps/api/rest/v0/__init__.py, backend/apps/api/rest/v0/pagination.py, backend/settings/base.py
Adds RouterPaginated default router to api_settings, bumps version to 0.2.4, introduces CustomPagination (Input/Output) and sets NINJA_PAGINATION_CLASS / NINJA_PAGINATION_PER_PAGE.
REST v0 endpoints (bulk schema & router refactor)
backend/apps/api/rest/v0/{chapter,committee,event,issue,label,member,organization,project,release,repository,sponsor}.py
Replaces Router()/PageNumberPagination with RouterPaginated(tags=[...]); splits schemas into *Base, * (list), *Detail, *Error, *Filter; changes list return types and filter params; adds/adjusts detail endpoints returning `*Detail
Tests updated to new schemas
backend/tests/apps/api/rest/v0/*.py
Tests updated to import/use *Detail (some via from_orm), accommodate added fields (e.g., key), and adjust assertions/order to match new schemas.
GitHub models — indexes
backend/apps/github/models/issue.py, backend/apps/github/models/release.py, backend/apps/github/models/repository.py
Adds index on Issue.number; renames/annotates Release created_at index and adds Release.tag_name index; adds Repository.name index.
OWASP models — index
backend/apps/owasp/models/event.py
Adds descending index on end_date (event_end_date_desc_idx).
Migrations for indexes
backend/apps/github/migrations/0037_...py, backend/apps/owasp/migrations/0054_event_event_end_date_desc_idx.py
Adds migration to rename/add GitHub indexes and migration to add Event -end_date index.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • kasya
  • aramattamara

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title “Extend v0 API” succinctly and accurately conveys the main purpose of this changeset by indicating that the version-0 API endpoints are being expanded across multiple modules, and it avoids unnecessary detail or ambiguity.
Description Check ✅ Passed The pull request description clearly states that the change involves extending the v0 API endpoints and includes a relevant checklist confirming tests and guidelines compliance, directly aligning with the changeset content.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ark/api-extend-v0

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc4b5e2 and b66da4d.

📒 Files selected for processing (11)
  • backend/tests/apps/api/rest/v0/chapter_test.py (1 hunks)
  • backend/tests/apps/api/rest/v0/committee_test.py (1 hunks)
  • backend/tests/apps/api/rest/v0/event_test.py (1 hunks)
  • backend/tests/apps/api/rest/v0/issue_test.py (1 hunks)
  • backend/tests/apps/api/rest/v0/label_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/member_test.py (1 hunks)
  • backend/tests/apps/api/rest/v0/organization_test.py (1 hunks)
  • backend/tests/apps/api/rest/v0/project_test.py (1 hunks)
  • backend/tests/apps/api/rest/v0/release_test.py (1 hunks)
  • backend/tests/apps/api/rest/v0/repository_test.py (1 hunks)
  • backend/tests/apps/api/rest/v0/sponsor_test.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/tests/apps/api/rest/v0/member_test.py
  • backend/tests/apps/api/rest/v0/repository_test.py
  • backend/tests/apps/api/rest/v0/committee_test.py
🧰 Additional context used
🧬 Code graph analysis (8)
backend/tests/apps/api/rest/v0/event_test.py (1)
backend/apps/api/rest/v0/event.py (1)
  • EventDetail (34-37)
backend/tests/apps/api/rest/v0/organization_test.py (1)
backend/apps/api/rest/v0/organization.py (1)
  • OrganizationDetail (33-37)
backend/tests/apps/api/rest/v0/chapter_test.py (2)
backend/apps/api/rest/v0/chapter.py (1)
  • ChapterDetail (38-42)
backend/apps/github/models/repository.py (1)
  • nest_key (152-154)
backend/tests/apps/api/rest/v0/project_test.py (2)
backend/apps/github/models/repository.py (2)
  • project (162-164)
  • nest_key (152-154)
backend/apps/api/rest/v0/project.py (1)
  • ProjectDetail (40-43)
backend/tests/apps/api/rest/v0/sponsor_test.py (1)
backend/apps/api/rest/v0/sponsor.py (1)
  • SponsorDetail (33-39)
backend/tests/apps/api/rest/v0/issue_test.py (1)
backend/apps/api/rest/v0/issue.py (1)
  • IssueDetail (35-38)
backend/tests/apps/api/rest/v0/release_test.py (1)
backend/apps/api/rest/v0/release.py (1)
  • ReleaseDetail (33-36)
backend/tests/apps/api/rest/v0/label_test.py (1)
backend/apps/api/rest/v0/label.py (1)
  • LabelDetail (28-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend e2e tests
🔇 Additional comments (8)
backend/tests/apps/api/rest/v0/issue_test.py (1)

1-38: LGTM!

The test correctly migrates to IssueDetail and validates all expected fields.

backend/tests/apps/api/rest/v0/release_test.py (1)

1-35: LGTM!

The test correctly migrates to ReleaseDetail and validates all expected fields.

backend/tests/apps/api/rest/v0/label_test.py (1)

1-27: LGTM!

The test correctly migrates to LabelDetail and validates all expected fields.

backend/tests/apps/api/rest/v0/project_test.py (1)

1-43: LGTM!

The test correctly migrates to ProjectDetail using the from_orm pattern with a mock object. This pattern differs from other tests (issue, release, label) that use direct instantiation, suggesting ProjectDetail requires ORM mode for attribute mapping.

backend/tests/apps/api/rest/v0/chapter_test.py (1)

1-43: LGTM!

The test correctly migrates to ChapterDetail using the from_orm pattern with a mock object, consistent with the project test approach.

backend/tests/apps/api/rest/v0/event_test.py (1)

1-37: LGTM!

The test correctly migrates to EventDetail using direct instantiation. Note that the previous review comment about from_orm compatibility is now outdated, as the current implementation uses direct instantiation with EventDetail(**event_data) rather than EventDetail.from_orm(MockEvent(...)).

backend/tests/apps/api/rest/v0/organization_test.py (1)

1-38: LGTM!

The test correctly migrates to OrganizationDetail and validates all expected fields.

backend/tests/apps/api/rest/v0/sponsor_test.py (1)

1-65: LGTM!

The tests correctly migrate to SponsorDetail and validate all expected fields in both comprehensive and minimal data scenarios.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/apps/api/rest/v0/organization.py (1)

34-101: Mark nullable organization fields as optional.

OrganizationDetail.company and OrganizationDetail.location are typed as str, yet the backing Organization model allows these fields to be blank/null (GitHub frequently returns them empty). When a stored row has NULL for either column the endpoint will now raise a 500 during response serialization. Please annotate them as optional (str | None) before release.

🧹 Nitpick comments (5)
backend/apps/api/rest/v0/member.py (2)

75-78: Ordering field should validate against model fields.

The ordering parameter accepts string literals but doesn't validate whether these fields exist on the UserModel. If the model structure changes, this could silently break ordering without errors.

Consider adding validation or using a more type-safe approach:

from typing import Literal

OrderingField = Literal["created_at", "-created_at", "updated_at", "-updated_at"]

# Then use:
ordering: OrderingField | None = Query(
    None,
    description="Ordering field",
)

Additionally, generate a verification script to confirm these fields exist on the User model:

#!/bin/bash
# Verify that ordering fields exist on the User model
ast-grep --pattern 'class User($$$):
  $$$
  created_at = $$$
  $$$
  updated_at = $$$
  $$$'

99-102: Consider extracting repeated error response pattern.

The error response pattern Response({"message": "..."}, status=HTTPStatus.NOT_FOUND) appears to be a common pattern that could be reused across endpoints.

Consider creating a helper function:

def not_found_response(resource: str) -> Response:
    """Return a standardized 404 response."""
    return Response(
        {"message": f"{resource} not found"}, 
        status=HTTPStatus.NOT_FOUND
    )

# Then use:
return not_found_response("Member")
backend/tests/apps/api/rest/v0/event_test.py (1)

30-36: MockEvent implementation could be more explicit.

The MockEvent class dynamically sets attributes from the data dict, then explicitly sets nest_key = data["key"]. This pattern works but could be clearer.

Consider making the mock more explicit about which attributes it provides:

class MockEvent:
    """Mock event object for testing ORM serialization."""
    def __init__(self, data):
        self.key = data["key"]
        self.name = data["name"]
        self.description = data["description"]
        self.url = data["url"]
        self.end_date = data["end_date"]
        self.start_date = data["start_date"]
        self.nest_key = data["key"]

Alternatively, if this pattern is used across multiple tests, extract it to a shared test utility.

#!/bin/bash
# Check if similar MockEvent patterns exist in other test files
rg -n "class Mock\w+:" backend/tests/apps/api/rest/v0/ -A 5
backend/tests/apps/api/rest/v0/chapter_test.py (1)

30-36: MockChapter duplicates pattern from MockEvent.

This MockChapter class follows the same pattern as MockEvent in event_test.py. Consider extracting this to a shared test utility to reduce code duplication.

Create a generic mock factory in a shared test utilities module:

# backend/tests/apps/api/rest/v0/conftest.py or test_utils.py
def create_mock_orm_object(data: dict, nest_key_field: str = "key"):
    """Create a mock ORM object for testing schema serialization.
    
    Args:
        data: Dictionary of field values
        nest_key_field: Field name to use for nest_key property
        
    Returns:
        Mock object with attributes set from data and nest_key property
    """
    class MockORM:
        def __init__(self, data, nest_key_field):
            for key, value in data.items():
                setattr(self, key, value)
            self.nest_key = data[nest_key_field]
    
    return MockORM(data, nest_key_field)

# Then use in tests:
chapter = ChapterDetail.from_orm(create_mock_orm_object(chapter_data))

Verify if other tests use this pattern:

#!/bin/bash
# Find all Mock classes in test files
rg -n "class Mock\w+:" backend/tests/apps/api/rest/v0/ -B 1 -A 5
backend/apps/api/rest/v0/committee.py (1)

79-84: Rename handler to match committee endpoint.

The detail handler is still called get_chapter and the docstring says “Get chapter”, which is confusing now that this file serves committees. Please rename the function (and docstring) to get_committee to align with the route, operation_id, and summary for maintainability.

Apply this diff:

-@router.get(
+@router.get(
@@
-def get_chapter(
+def get_committee(
     request: HttpRequest,
     committee_id: str = Path(example="project"),
 ) -> CommitteeDetail | CommitteeError:
-    """Get chapter."""
+    """Get committee."""
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d684e1f and 693030c.

📒 Files selected for processing (31)
  • backend/apps/api/rest/v0/__init__.py (2 hunks)
  • backend/apps/api/rest/v0/chapter.py (1 hunks)
  • backend/apps/api/rest/v0/committee.py (1 hunks)
  • backend/apps/api/rest/v0/event.py (1 hunks)
  • backend/apps/api/rest/v0/issue.py (1 hunks)
  • backend/apps/api/rest/v0/label.py (2 hunks)
  • backend/apps/api/rest/v0/member.py (1 hunks)
  • backend/apps/api/rest/v0/organization.py (3 hunks)
  • backend/apps/api/rest/v0/pagination.py (1 hunks)
  • backend/apps/api/rest/v0/project.py (1 hunks)
  • backend/apps/api/rest/v0/release.py (1 hunks)
  • backend/apps/api/rest/v0/repository.py (1 hunks)
  • backend/apps/api/rest/v0/sponsor.py (2 hunks)
  • backend/apps/github/migrations/0037_rename_github_rele_created_d51966_idx_release_created_at_desc_idx_and_more.py (1 hunks)
  • backend/apps/github/models/issue.py (1 hunks)
  • backend/apps/github/models/release.py (1 hunks)
  • backend/apps/github/models/repository.py (1 hunks)
  • backend/apps/owasp/migrations/0054_event_event_end_date_desc_idx.py (1 hunks)
  • backend/apps/owasp/models/event.py (1 hunks)
  • backend/settings/base.py (1 hunks)
  • backend/tests/apps/api/rest/v0/chapter_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/committee_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/event_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/issue_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/label_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/member_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/organization_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/project_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/release_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/repository_test.py (2 hunks)
  • backend/tests/apps/api/rest/v0/sponsor_test.py (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (23)
backend/tests/apps/api/rest/v0/issue_test.py (1)
backend/apps/api/rest/v0/issue.py (1)
  • IssueDetail (35-38)
backend/apps/owasp/migrations/0054_event_event_end_date_desc_idx.py (1)
backend/apps/github/migrations/0037_rename_github_rele_created_d51966_idx_release_created_at_desc_idx_and_more.py (1)
  • Migration (6-29)
backend/tests/apps/api/rest/v0/committee_test.py (3)
backend/apps/owasp/api/internal/queries/committee.py (1)
  • committee (14-28)
backend/apps/api/rest/v0/committee.py (1)
  • CommitteeDetail (38-41)
backend/apps/github/models/repository.py (1)
  • nest_key (152-154)
backend/apps/api/rest/v0/__init__.py (1)
backend/apps/api/rest/auth/api_key.py (1)
  • ApiKey (11-35)
backend/tests/apps/api/rest/v0/label_test.py (1)
backend/apps/api/rest/v0/label.py (1)
  • LabelDetail (28-31)
backend/tests/apps/api/rest/v0/event_test.py (2)
backend/apps/api/rest/v0/event.py (1)
  • EventDetail (34-37)
backend/apps/github/models/repository.py (1)
  • nest_key (152-154)
backend/tests/apps/api/rest/v0/member_test.py (1)
backend/apps/api/rest/v0/member.py (1)
  • MemberDetail (35-45)
backend/tests/apps/api/rest/v0/organization_test.py (1)
backend/apps/api/rest/v0/organization.py (1)
  • OrganizationDetail (33-37)
backend/tests/apps/api/rest/v0/repository_test.py (2)
backend/apps/github/api/internal/queries/repository.py (1)
  • repository (14-35)
backend/apps/api/rest/v0/repository.py (1)
  • RepositoryDetail (32-35)
backend/tests/apps/api/rest/v0/project_test.py (2)
backend/apps/github/models/repository.py (2)
  • project (162-164)
  • nest_key (152-154)
backend/apps/api/rest/v0/project.py (1)
  • ProjectDetail (40-43)
backend/apps/api/rest/v0/event.py (1)
backend/apps/owasp/models/event.py (1)
  • Event (25-310)
backend/apps/api/rest/v0/project.py (4)
backend/apps/github/models/repository.py (2)
  • project (162-164)
  • nest_key (152-154)
backend/apps/owasp/models/enums/project.py (1)
  • ProjectLevel (37-44)
backend/apps/api/rest/v0/chapter.py (1)
  • resolve_key (29-31)
backend/apps/api/rest/v0/committee.py (1)
  • resolve_key (29-31)
backend/tests/apps/api/rest/v0/chapter_test.py (2)
backend/apps/api/rest/v0/chapter.py (1)
  • ChapterDetail (38-42)
backend/apps/github/models/repository.py (1)
  • nest_key (152-154)
backend/apps/api/rest/v0/issue.py (2)
backend/apps/github/models/generic_issue_model.py (3)
  • GenericIssueModel (10-77)
  • State (18-20)
  • repository_id (70-77)
backend/apps/github/models/issue.py (1)
  • Issue (18-213)
backend/apps/api/rest/v0/release.py (1)
backend/apps/github/models/release.py (2)
  • Release (10-135)
  • summary (59-61)
backend/apps/api/rest/v0/chapter.py (4)
backend/apps/owasp/api/internal/queries/chapter.py (1)
  • chapter (14-19)
backend/apps/api/rest/v0/committee.py (2)
  • resolve_key (29-31)
  • get_chapter (79-94)
backend/apps/api/rest/v0/project.py (1)
  • resolve_key (31-33)
backend/apps/github/models/repository.py (1)
  • nest_key (152-154)
backend/apps/api/rest/v0/committee.py (3)
backend/apps/owasp/api/internal/queries/committee.py (1)
  • committee (14-28)
backend/apps/api/rest/v0/chapter.py (2)
  • resolve_key (29-31)
  • get_chapter (87-99)
backend/apps/github/models/repository.py (1)
  • nest_key (152-154)
backend/apps/github/migrations/0037_rename_github_rele_created_d51966_idx_release_created_at_desc_idx_and_more.py (1)
backend/apps/owasp/migrations/0054_event_event_end_date_desc_idx.py (1)
  • Migration (6-16)
backend/tests/apps/api/rest/v0/release_test.py (1)
backend/apps/api/rest/v0/release.py (1)
  • ReleaseDetail (33-36)
backend/apps/api/rest/v0/repository.py (2)
backend/apps/github/api/internal/queries/repository.py (1)
  • repository (14-35)
backend/apps/github/models/repository.py (1)
  • Repository (25-352)
backend/tests/apps/api/rest/v0/sponsor_test.py (1)
backend/apps/api/rest/v0/sponsor.py (1)
  • SponsorDetail (33-39)
backend/apps/api/rest/v0/sponsor.py (1)
backend/apps/owasp/models/sponsor.py (1)
  • MemberType (29-32)
backend/apps/api/rest/v0/member.py (1)
backend/apps/mentorship/api/internal/nodes/mentor.py (3)
  • avatar_url (13-15)
  • login (23-25)
  • name (18-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend unit tests
🔇 Additional comments (13)
backend/apps/owasp/migrations/0054_event_event_end_date_desc_idx.py (1)

1-16: LGTM!

The migration correctly implements the descending index on end_date that was added to the Event model. The dependency chain and index definition are accurate.

backend/apps/owasp/models/event.py (1)

32-32: Confirm end_date index usage or remove unused index. We didn’t detect any order_by or filter on end_date in the v0 API; verify that these endpoints will leverage this index or remove it if unused.

backend/apps/api/rest/v0/member.py (3)

54-61: Filter schema is well-structured.

The MemberFilter schema with company and location fields provides useful filtering capabilities with clear descriptions.


81-81: Verify presence and behavior of FilterSchema.filter
I couldn’t locate a FilterSchema definition in the codebase; confirm your filters.filter(UserModel.objects.order_by(...)) call uses a valid schema whose .filter() method correctly applies filters to the queryset.


31-32: Empty Member schema is intentional
The Member class extends MemberBase solely to represent the list‐view schema, mirroring other modules (e.g., Sponsor, Repository) and its docstring already documents the minimal‐fields design.

Likely an incorrect or invalid review comment.

backend/tests/apps/api/rest/v0/member_test.py (3)

5-5: LGTM! Import updated to match new schema.

The test correctly imports MemberDetail to align with the API refactoring.


32-32: LGTM! Schema construction updated correctly.

The test now uses MemberDetail which matches the updated API surface.


49-49: Good security practice: verifying sensitive field exclusion.

The test explicitly verifies that email is not exposed in the schema, which is an important security check.

backend/tests/apps/api/rest/v0/issue_test.py (1)

5-5: LGTM! Test updated to use new schema.

The test correctly uses IssueDetail to align with the API refactoring. All assertions remain valid.

Also applies to: 31-31

backend/tests/apps/api/rest/v0/label_test.py (1)

3-3: LGTM! Test updated to use new schema.

The test correctly uses LabelDetail to align with the API refactoring. All assertions remain valid.

Also applies to: 23-23

backend/apps/github/models/repository.py (1)

33-35: Confirm index addition and migration

Index on the name field and its migration exist.

backend/settings/base.py (1)

130-131: Confirm django-ninja pagination settings
CustomPagination exists and is correctly implemented. Ensure NINJA_PAGINATION_CLASS and NINJA_PAGINATION_PER_PAGE match the global setting names defined by django-ninja.

backend/tests/apps/api/rest/v0/chapter_test.py (1)

37-37: Verify ORM mode configuration on ChapterDetail
Ensure Schema.Config.orm_mode = True (inherited by ChapterDetail) so that ChapterDetail.from_orm(MockChapter(...)) functions correctly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
backend/apps/api/rest/v0/event.py (1)

65-83: Endpoint implementation depends on ORM mode configuration.

The endpoint correctly uses response mapping for different status codes. However, line 81 returns a model instance directly, which requires ORM mode (flagged earlier).

Optional: Consider using tuple return for cleaner status code handling:

-    return Response({"message": "Event not found"}, status=HTTPStatus.NOT_FOUND)
+    return HTTPStatus.NOT_FOUND, EventError(message="Event not found")

This leverages django-ninja's automatic status code handling without the explicit Response wrapper.

backend/apps/api/rest/v0/repository.py (2)

32-36: Consider enriching the detail schema.

RepositoryDetail only adds description beyond the base fields. The underlying Repository model exposes 30+ fields including key, languages, topics, license, size, stars_count, forks_count, and various boolean flags (is_archived, is_fork, etc.). For a detail endpoint, users typically expect richer information.

If minimal fields are intentional for v0 API simplicity, document this design choice. Otherwise, consider adding commonly requested fields:

 class RepositoryDetail(RepositoryBase):
     """Detail schema for Repository (used in single item endpoints)."""

     description: str | None = None
+    key: str
+    languages: dict = {}
+    topics: list[str] = []
+    license: str = ""
+    stars_count: int = 0
+    forks_count: int = 0
+    is_archived: bool = False

96-99: Use key__iexact instead of name__iexact in repository lookup (backend/apps/api/rest/v0/repository.py:96–99).
key is always name.lower(), so this is functionally equivalent and aligns with the internal API pattern.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 693030c and cc4b5e2.

📒 Files selected for processing (5)
  • backend/apps/api/rest/v0/event.py (1 hunks)
  • backend/apps/api/rest/v0/pagination.py (1 hunks)
  • backend/apps/api/rest/v0/release.py (1 hunks)
  • backend/apps/api/rest/v0/repository.py (1 hunks)
  • backend/tests/apps/api/rest/v0/event_test.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/api/rest/v0/pagination.py
🧰 Additional context used
🧬 Code graph analysis (4)
backend/apps/api/rest/v0/event.py (1)
backend/apps/owasp/models/event.py (1)
  • Event (25-310)
backend/apps/api/rest/v0/repository.py (2)
backend/apps/github/api/internal/queries/repository.py (1)
  • repository (14-35)
backend/apps/github/models/repository.py (1)
  • Repository (25-352)
backend/tests/apps/api/rest/v0/event_test.py (1)
backend/apps/api/rest/v0/event.py (1)
  • EventDetail (34-37)
backend/apps/api/rest/v0/release.py (1)
backend/apps/github/models/release.py (2)
  • Release (10-135)
  • summary (59-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend unit tests
🔇 Additional comments (10)
backend/apps/api/rest/v0/release.py (3)

20-27: Past review comment addressed correctly.

The published_at field is now properly typed as datetime | None = None, which aligns with the model reality where this field can be null for draft releases or failed syncs. This resolves the previous 500 error risk during serialization.


78-94: Well-implemented list endpoint with good performance considerations.

The queryset correctly:

  • Excludes draft releases (null published_at)
  • Uses select_related to avoid N+1 queries
  • Applies case-insensitive filters for organization and repository
  • Provides deterministic ordering with secondary sort

115-119: Verify intended behavior: detail endpoint can return draft releases.

The list_release endpoint (line 79) explicitly excludes releases with null published_at, ensuring only published releases appear in lists. However, get_release does not apply this filter, allowing draft releases to be fetched directly.

This creates an API inconsistency where:

  • List: only published releases
  • Detail: includes draft releases

If this is intentional (e.g., for admin preview or direct-link access), consider documenting it. Otherwise, apply the same published_at__isnull=False filter here for consistency.

If the filter should be applied, use this diff:

     try:
-        return ReleaseModel.objects.get(
+        return ReleaseModel.objects.exclude(published_at__isnull=True).get(
             repository__organization__login__iexact=organization_id,
             repository__name__iexact=repository_id,
             tag_name=release_id,
         )
backend/tests/apps/api/rest/v0/event_test.py (1)

29-43: LGTM! Clean test implementation.

The test correctly validates the EventDetail schema with proper datetime conversion and comprehensive field assertions. The direct instantiation approach is cleaner than the previous .from_orm() pattern.

backend/apps/api/rest/v0/event.py (4)

30-32: LGTM!

Clean minimal schema for list display, correctly inheriting from EventBase.


34-37: LGTM!

Correctly extends EventBase with the optional description field for detail endpoints.


40-43: LGTM!

Standard error response schema following common patterns.


46-62: Verify ORM mode is configured before deployment.

The endpoint returns a QuerySet directly (line 62), which django-ninja will serialize using the Event schema. This requires ORM mode to be configured on EventBase (flagged in the previous comment).

backend/apps/api/rest/v0/repository.py (2)

35-35: Note on nullable description type hint.

The type hint str | None addresses the past review concern about nullable descriptions. However, the underlying model field (Repository.description) is defined as CharField(blank=True) without null=True, meaning the database stores empty strings ("") rather than NULL. The type hint is more permissive than necessary but provides defensive handling if the model changes later.

Based on past review comments.


54-76: LGTM! Well-optimized list endpoint.

The endpoint correctly uses select_related("organization") to avoid N+1 queries and applies a secondary sort by updated_at for deterministic ordering when the primary field has duplicates. The filter integration and response typing are appropriate.

@arkid15r arkid15r marked this pull request as ready for review October 5, 2025 18:08
@arkid15r arkid15r requested a review from kasya as a code owner October 5, 2025 18:08
@arkid15r arkid15r enabled auto-merge October 5, 2025 23:25
@sonarqubecloud
Copy link

sonarqubecloud bot commented Oct 5, 2025

Copy link
Collaborator

@kasya kasya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@arkid15r arkid15r added this pull request to the merge queue Oct 5, 2025
Merged via the queue into main with commit b577fb1 Oct 5, 2025
25 checks passed
@arkid15r arkid15r deleted the ark/api-extend-v0 branch October 5, 2025 23:43
@coderabbitai coderabbitai bot mentioned this pull request Nov 1, 2025
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants