-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Summary
Implement a fluent IssueQuery builder that provides an expressive, Laravel-style API for filtering and querying GitHub issues. This should follow the established patterns from conduit-ui/repo (RepositoryQuery, BranchQuery).
Requirements
Query Building Pattern
use ConduitUI\Issue\Facades\Issues;
// Basic queries
Issues::forRepo('owner/repo')
->whereOpen()
->get();
Issues::forRepo('owner/repo')
->whereClosed()
->get();
// Label filtering
Issues::forRepo('owner/repo')
->whereLabel('bug')
->get();
Issues::forRepo('owner/repo')
->whereLabels(['bug', 'priority-high'])
->get();
// Assignee filtering
Issues::forRepo('owner/repo')
->assignedTo('username')
->get();
Issues::forRepo('owner/repo')
->whereUnassigned()
->get();
// Author filtering
Issues::forRepo('owner/repo')
->createdBy('username')
->get();
// Time-based filtering
Issues::forRepo('owner/repo')
->createdAfter(now()->subWeek())
->get();
Issues::forRepo('owner/repo')
->updatedBefore(now()->subDays(90))
->get();
Issues::forRepo('owner/repo')
->older(days: 90)
->get();
// Sorting
Issues::forRepo('owner/repo')
->orderByCreated('desc')
->get();
Issues::forRepo('owner/repo')
->orderByUpdated('asc')
->get();
// Pagination
Issues::forRepo('owner/repo')
->perPage(50)
->page(2)
->get();
// Complex queries
Issues::forRepo('owner/repo')
->whereOpen()
->whereLabel('bug')
->assignedTo('jordan')
->createdAfter(now()->subWeek())
->orderByCreated('desc')
->get();Implementation Details
Class Structure:
ConduitUI\Issue\Services\IssueQuery- Constructor should accept
GitHub $github(from conduit-ui/connector) - All query methods return
$thisfor chaining - Final
get()method returnsCollectionofIssueDTOs
Query Methods:
forRepo(string $fullName): self- Set repository contextwhereOpen(): self- Filter to open issueswhereClosed(): self- Filter to closed issueswhereState(string $state): self- Filter by state (open|closed|all)whereLabel(string $label): self- Filter by single labelwhereLabels(array $labels): self- Filter by multiple labelsassignedTo(string $username): self- Filter by assigneewhereUnassigned(): self- Filter to unassigned issuescreatedBy(string $username): self- Filter by authormentioning(string $username): self- Filter by mentioned usercreatedAfter(Carbon|string $date): self- Filter by creation dateupdatedBefore(Carbon|string $date): self- Filter by update dateolder(int $days): self- Convenience method for stale issuesorderBy(string $field, string $direction = 'desc'): self- Sort resultsorderByCreated(string $direction = 'desc'): self- Convenience methodorderByUpdated(string $direction = 'desc'): self- Convenience methodperPage(int $perPage): self- Set pagination limitpage(int $page): self- Set page numberget(): Collection- Execute query and return resultscount(): int- Get count of matching issuesexists(): bool- Check if any issues matchfirst(): ?Issue- Get first matching issue
Protected Helper Methods:
buildEndpoint(): string- Build API endpointbuildParams(): array- Build query parameters
GitHub API Mapping
Reference: https://docs.github.com/en/rest/issues/issues#list-repository-issues
Query Parameters:
state→ open, closed, all (default: open)labels→ comma-separated listassignee→ username or "none"creator→ usernamementioned→ usernamesince→ ISO 8601 timestampsort→ created, updated, comments (default: created)direction→ asc, desc (default: desc)per_page→ 1-100 (default: 30)page→ page number
Return Type
use ConduitUI\Issue\Data\Issue;
use Illuminate\Support\Collection;
/** @return Collection<int, Issue> */
public function get(): CollectionTesting Requirements
Unit Tests:
- Test each filter method sets correct internal state
- Test method chaining returns self
- Test buildParams() generates correct query parameters
- Test buildEndpoint() generates correct API path
Integration Tests:
- Test get() returns Collection of Issue DTOs
- Test filtering by labels
- Test filtering by assignee
- Test time-based filtering
- Test sorting
- Test pagination
- Test complex multi-filter queries
Related Files to Reference
From conduit-ui/repo:
src/Services/RepositoryQuery.php- Query patternsrc/Services/BranchQuery.php- Advanced filtering examplesrc/Services/CommitQuery.php- Time-based filtering
Acceptance Criteria
- IssueQuery class implements all query methods
- All methods return self for chaining
- get() method returns Collection of Issue DTOs
- Query parameters correctly map to GitHub API
- Time-based filtering handles Carbon and string dates
- Full test coverage (unit + integration)
- Follows existing conduit-ui patterns
- Type hints and return types on all methods
- PHPDoc blocks on all public methods
Dependencies
- Requires:
ConduitUI\Connector\GitHub - Requires:
ConduitUI\Issue\Data\Issue(from Add Active Record entity layer (matching conduit-ui/pr pattern) #2) - Related to: 🔒 Security audit for production release #7 (Contracts)
Technical Notes
Pattern Consistency:
Follow the established patterns from conduit-ui/repo:
- Constructor injection of GitHub client
- Fluent method chaining
- Protected state properties
- Single get() execution method
- Helper methods for building endpoint/params
Date Handling:
use Carbon\Carbon;
protected function formatDate(Carbon|string $date): string
{
return $date instanceof Carbon
? $date->toIso8601String()
: Carbon::parse($date)->toIso8601String();
}Examples
Triage Bot:
Issues::forRepo('laravel/framework')
->whereOpen()
->whereUnassigned()
->whereLabel('bug')
->createdAfter(now()->subDay())
->get()
->each(fn($issue) => triageIssue($issue));Stale Issue Detection:
$staleIssues = Issues::forRepo('owner/repo')
->whereOpen()
->older(days: 90)
->get();Team Dashboard:
$myIssues = Issues::forRepo('owner/repo')
->whereOpen()
->assignedTo('jordan')
->orderByUpdated('asc')
->get();coderabbitai
Metadata
Metadata
Assignees
Labels
No labels