Skip to content

feat(api): implement GET /bounties/:id (bounty detail)#55

Open
genesis-ai-labs-star wants to merge 1 commit intodevasignhq:mainfrom
genesis-ai-labs-star:feat/bounty-21-get-bounty-detail
Open

feat(api): implement GET /bounties/:id (bounty detail)#55
genesis-ai-labs-star wants to merge 1 commit intodevasignhq:mainfrom
genesis-ai-labs-star:feat/bounty-21-get-bounty-detail

Conversation

@genesis-ai-labs-star
Copy link

Implements GET /bounties/:id returning full bounty details including creator info, application count, assignee info (when assigned), and status.\n\nRefs: #21

@bishopBethel
Copy link
Member

review

@devasign-app
Copy link

devasign-app bot commented Feb 21, 2026

🟡 AI Code Review Results

Status: Review Recommended
Confidence: 100%


🟡 Merge Score: 80/100

🟡 ████████████████░░░░ 80%

Recommendation: ⚠️ This PR is mostly good but could benefit from some improvements before merging.

The PR successfully implements the bounty detail endpoint with a secure, parameterized SQL query and good use of dependency injection for testability. Key areas for improvement include adding UUID validation for the bounty ID and refactoring the database-to-API response mapping into a reusable function to enhance maintainability.

💡 Code Suggestions (3)

🔴 High Priority (1)

  1. packages/api/src/app.ts (Line 71)
    🔧 The bountyId from the URL parameter should be validated to ensure it's in a valid UUID format before being used in a database query. This is a security best practice (defense-in-depth) and improves robustness by rejecting invalid requests early. The existing check for !bountyId is redundant as the router guarantees the parameter exists on a matched route.

💭 Reasoning: This enforces a strict contract for the endpoint's input, improving security and robustness by failing fast on invalid data and preventing malformed IDs from reaching the database.

Suggested Code:

const bountyId = c.req.param('id');
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

if (!uuidRegex.test(bountyId)) {
    return c.json({ error: 'Invalid bounty ID format' }, 400);
}

🟡 Medium Priority (2)

  1. packages/api/src/app.ts (Line 126)
    ✨ The manual mapping from the snake_case database row to the camelCase API response object is verbose and will likely be duplicated in other parts of the API. Extracting this into a dedicated utility function improves code reuse, testability, and maintainability.

💭 Reasoning: This abstraction separates the data transformation logic from the request handling flow, adhering to the Single Responsibility Principle and making the code easier to manage and test as the API grows.

Suggested Code:

// Define this helper function within app.ts or in a shared utils file
function mapDbBountyToApiBounty(row: any) {
    return {
        id: row.id,
        githubIssueId: row.github_issue_id,
        repoOwner: row.repo_owner,
        repoName: row.repo_name,
        title: row.title,
        description: row.description,
        amountUsdc: row.amount_usdc,
        techTags: row.tech_tags,
        difficulty: row.difficulty,
        status: row.status,
        deadline: row.deadline,
        createdAt: row.created_at,
        updatedAt: row.updated_at,
        creator: {
            id: row.creator_id,
            username: row.creator_username,
            avatarUrl: row.creator_avatar_url,
        },
        applicationCount: row.application_count ?? 0,
        assignee: row.assignee_id
            ? {
                  id: row.assignee_id,
                  username: row.assignee_username,
                  avatarUrl: row.assignee_avatar_url,
              }
            : null,
    };
}

// ... inside the handler, replace the large c.json block with this:
return c.json(mapDbBountyToApiBounty(row));
  1. packages/api/src/tests/app.test.ts (Line 105)
    ✨ The test suite is missing a case to verify the correct handling of bounties that do not have an assignee. Adding this test ensures that the assignee: null logic is working as expected and prevents future regressions.

💭 Reasoning: Comprehensive testing includes covering both primary paths and common edge cases. This test case validates the correct behavior for an unassigned bounty, which is a critical state in the data model.

Suggested Code:

it('should return assignee as null when not assigned', async () => {
    const unassignedDb: DbLike = {
        execute: async () => ({
            rows: [
                {
                    id: '11111111-1111-1111-1111-111111111111',
                    github_issue_id: 123,
                    repo_owner: 'acme',
                    repo_name: 'repo',
                    title: 'Test bounty',
                    description: 'Desc',
                    amount_usdc: '100',
                    tech_tags: ['ts'],
                    difficulty: 'beginner',
                    status: 'open',
                    deadline: null,
                    creator_id: '22222222-2222-2222-2222-222222222222',
                    created_at: '2026-02-19T00:00:00.000Z',
                    updated_at: '2026-02-19T00:00:00.000Z',
                    creator_username: 'creator',
                    creator_avatar_url: 'https://example.com/c.png',
                    assignee_id: null,
                    assignee_username: null,
                    assignee_avatar_url: null,
                    application_count: 3,
                },
            ],
        }),
    };

    const app3 = createApp({ db: unassignedDb });
    const res = await app3.request('/bounties/11111111-1111-1111-1111-111111111111');
    expect(res.status).toBe(200);
    const body = await res.json();

    expect(body.id).toBe('11111111-1111-1111-1111-111111111111');
    expect(body.assignee).toBeNull();
});
📊 Review Metadata
  • Processing Time: 180s
  • Analysis Date: 2/21/2026, 6:55:10 AM

🤖 This review was generated by AI. While we strive for accuracy, please use your judgment when applying suggestions.

💬 Questions about this review? Open an issue or contact support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants