Skip to content

Core Contracts: Define all package interfaces #27

@jordanpartridge

Description

@jordanpartridge

Summary

Define comprehensive interface contracts for the entire package following interface-first design pattern, ensuring clean architecture and testability.

Acceptance Criteria

Core Query Interfaces

Create src/Contracts/ directory with all interface definitions:

namespace ConduitUI\Pr\Contracts;

use Illuminate\Support\Collection;
use ConduitUI\Pr\Data\PullRequest;

/**
 * Contract for querying pull requests
 */
interface PullRequestQueryInterface
{
    public function whereOpen(): self;
    public function whereClosed(): self;
    public function whereMerged(): self;
    public function whereState(string $state): self;
    public function whereBase(string $branch): self;
    public function whereHead(string $branch): self;
    public function whereAuthor(string $username): self;
    public function whereLabel(string $label): self;
    public function whereLabels(array $labels): self;
    public function whereDraft(bool $draft = true): self;
    public function orderByCreated(string $direction = 'desc'): self;
    public function orderByUpdated(string $direction = 'desc'): self;
    public function perPage(int $count): self;
    public function page(int $page): self;
    public function get(): Collection;
    public function first(): ?PullRequest;
    public function count(): int;
    public function exists(): bool;
}

/**
 * Contract for managing pull requests
 */
interface PullRequestManagerInterface
{
    public function find(int $number): PullRequest;
    public function get(int $number): PullRequest;
    public function query(): PullRequestQueryInterface;
    public function create(): PullRequestBuilderInterface;
}

/**
 * Contract for creating pull requests
 */
interface PullRequestBuilderInterface
{
    public function title(string $title): self;
    public function body(string $body): self;
    public function head(string $branch): self;
    public function base(string $branch): self;
    public function draft(bool $draft = true): self;
    public function maintainerCanModify(bool $allowed = true): self;
    public function create(): PullRequest;
}

Review Interfaces

namespace ConduitUI\Pr\Contracts;

use ConduitUI\Pr\Data\Review;
use Illuminate\Support\Collection;

/**
 * Contract for creating reviews
 */
interface ReviewBuilderInterface
{
    public function approve(string $comment = null): self;
    public function requestChanges(string $comment = null): self;
    public function comment(string $body): self;
    public function addInlineComment(string $path, int $line, string $comment): self;
    public function addSuggestion(string $path, int $startLine, int $endLine, string $suggestion): self;
    public function submit(): Review;
}

/**
 * Contract for querying reviews
 */
interface ReviewQueryInterface
{
    public function get(): Collection;
    public function whereApproved(): Collection;
    public function whereChangesRequested(): Collection;
    public function wherePending(): Collection;
    public function byUser(string $username): Collection;
    public function latest(): ?Review;
}

Check Run Interfaces

namespace ConduitUI\Pr\Contracts;

use ConduitUI\Pr\Data\CheckRun;
use ConduitUI\Pr\Data\CheckSummary;
use Illuminate\Support\Collection;

/**
 * Contract for querying check runs
 */
interface CheckRunQueryInterface
{
    public function get(): Collection;
    public function wherePassing(): Collection;
    public function whereFailing(): Collection;
    public function wherePending(): Collection;
    public function whereNeutral(): Collection;
    public function whereSkipped(): Collection;
    public function latest(): ?CheckRun;
    public function byName(string $name): ?CheckRun;
    public function summary(): CheckSummary;
}

Merge Interfaces

namespace ConduitUI\Pr\Contracts;

use ConduitUI\Pr\Data\PullRequest;

/**
 * Contract for merge operations
 */
interface MergeManagerInterface
{
    public function merge(?string $commitTitle = null, ?string $commitMessage = null): PullRequest;
    public function squash(?string $commitTitle = null, ?string $commitMessage = null): PullRequest;
    public function rebase(): PullRequest;
    public function canMerge(): bool;
    public function deleteBranch(): bool;
}

File Interfaces

namespace ConduitUI\Pr\Contracts;

use ConduitUI\Pr\Data\FileChange;
use ConduitUI\Pr\Data\FileStats;
use Illuminate\Support\Collection;

/**
 * Contract for querying changed files
 */
interface FileQueryInterface
{
    public function get(): Collection;
    public function whereAdded(): Collection;
    public function whereModified(): Collection;
    public function whereRemoved(): Collection;
    public function whereRenamed(): Collection;
    public function wherePath(string $pattern): Collection;
    public function whereExtension(string $extension): Collection;
    public function stats(): FileStats;
}

Action Interfaces

namespace ConduitUI\Pr\Contracts;

use ConduitUI\Pr\Data\PullRequest;

/**
 * Contract for PR state actions
 */
interface PullRequestActionsInterface
{
    public function close(string $comment = null): PullRequest;
    public function reopen(): PullRequest;
    public function markDraft(): PullRequest;
    public function markReady(): PullRequest;
    public function addLabel(string $label): self;
    public function addLabels(array $labels): self;
    public function removeLabel(string $label): self;
    public function setLabels(array $labels): self;
    public function requestReview(string $username): self;
    public function requestReviews(array $usernames): self;
    public function requestTeamReview(string $teamSlug): self;
    public function assign(string $username): self;
    public function unassign(string $username): self;
    public function comment(string $body): PullRequest;
}

Comment Interfaces

namespace ConduitUI\Pr\Contracts;

use ConduitUI\Pr\Data\Comment;
use Illuminate\Support\Collection;

/**
 * Contract for managing comments
 */
interface CommentManagerInterface
{
    public function get(): Collection;
    public function create(string $body): Comment;
    public function update(int $commentId, string $body): Comment;
    public function delete(int $commentId): bool;
}

Interface Design Principles

1. Fluent Chainability

All builder/action interfaces return self for chainable methods:

->title('foo')
->body('bar')
->draft()
->create()

2. Query Segregation

Separate query interfaces from command interfaces:

  • *QueryInterface - Read operations returning Collections
  • *BuilderInterface - Creation operations
  • *ManagerInterface - CRUD operations
  • *ActionsInterface - State-changing operations

3. Type Safety

All interfaces use strict typing:

public function find(int $number): PullRequest;
public function get(): Collection;
public function exists(): bool;

4. Nullable Returns

Use nullable types where operations may not return data:

public function first(): ?PullRequest;
public function latest(): ?Review;
public function byName(string $name): ?CheckRun;

5. Collection Returns

Query operations return Laravel Collections for filtering/mapping:

public function get(): Collection; // Collection<PullRequest>
public function whereApproved(): Collection; // Collection<Review>

Implementation Requirements

Directory Structure

src/
├── Contracts/
│   ├── PullRequestQueryInterface.php
│   ├── PullRequestManagerInterface.php
│   ├── PullRequestBuilderInterface.php
│   ├── PullRequestActionsInterface.php
│   ├── ReviewBuilderInterface.php
│   ├── ReviewQueryInterface.php
│   ├── CheckRunQueryInterface.php
│   ├── MergeManagerInterface.php
│   ├── FileQueryInterface.php
│   └── CommentManagerInterface.php
├── Services/
│   ├── PullRequestQuery.php (implements PullRequestQueryInterface)
│   ├── PullRequestManager.php (implements PullRequestManagerInterface)
│   ├── PullRequestBuilder.php (implements PullRequestBuilderInterface)
│   ├── PullRequestActions.php (implements PullRequestActionsInterface)
│   ├── ReviewBuilder.php (implements ReviewBuilderInterface)
│   ├── ReviewQuery.php (implements ReviewQueryInterface)
│   ├── CheckRunQuery.php (implements CheckRunQueryInterface)
│   ├── MergeManager.php (implements MergeManagerInterface)
│   ├── FileQuery.php (implements FileQueryInterface)
│   └── CommentManager.php (implements CommentManagerInterface)
└── Data/
    ├── PullRequest.php
    ├── Review.php
    ├── CheckRun.php
    ├── CheckSummary.php
    ├── FileChange.php
    ├── FileStats.php
    ├── Comment.php
    ├── User.php
    └── Label.php

Service Provider Bindings

namespace ConduitUI\Pr;

use Illuminate\Support\ServiceProvider;

class PrServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(
            Contracts\PullRequestManagerInterface::class,
            Services\PullRequestManager::class
        );
        
        $this->app->bind(
            Contracts\PullRequestQueryInterface::class,
            Services\PullRequestQuery::class
        );
        
        // ... bind all other interfaces
    }
}

Testing Requirements

Interface Compliance Tests

it('implements PullRequestQueryInterface')
    ->expect(PullRequestQuery::class)
    ->toImplement(PullRequestQueryInterface::class);

it('implements ReviewBuilderInterface')
    ->expect(ReviewBuilder::class)
    ->toImplement(ReviewBuilderInterface::class);

// Test all service classes implement their interfaces

Type Testing

it('returns Collection from query')
    ->expect(fn() => PullRequests::forRepo('test/repo')->query()->get())
    ->toBeInstanceOf(Collection::class);

it('returns PullRequest from find')
    ->expect(fn() => PullRequests::find('test/repo', 1))
    ->toBeInstanceOf(PullRequest::class);

Benefits

  1. Testability: Easy to mock interfaces for testing
  2. Extensibility: Swap implementations without changing consumer code
  3. Documentation: Interfaces serve as contracts and documentation
  4. Type Safety: IDE autocomplete and static analysis support
  5. Dependency Injection: Clean DI with interface bindings
  6. Architecture: Clear separation of concerns

Related Issues

References

Labels

  • enhancement
  • architecture
  • contracts
  • high-priority

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions