Skip to content

Latest commit

 

History

History
83 lines (60 loc) · 6.87 KB

ARCHITECTURE.md

File metadata and controls

83 lines (60 loc) · 6.87 KB

Architecture

jordanpartridge/github-client is a Laravel package for the GitHub REST API. It is built on Saloon and returns typed PHP objects instead of raw arrays. It requires PHP 8.2 or newer and Laravel 11, 12, or 13. CI runs the suite on PHP 8.2, 8.3, and 8.4.

Layers

Facade (Github::)  ->  Github client  ->  Resource (repos, pullRequests, issues, ...)
                                          ->  Request class (one per endpoint)
                                              ->  GithubConnector (base URL, headers, auth, errors)
                                              <-  createDtoFromResponse() -> typed Data object
Layer Responsibility Example
Connector Base URL, API version header, authentication, mapping HTTP errors to exceptions src/Connectors/GithubConnector.php
Requests One class per endpoint: method, path, query, and response mapping src/Requests/Pulls/Index.php
Resources Developer-facing methods grouped by API area src/Resources/RepoResource.php
Data Typed objects built from responses src/Data/Repos/RepoData.php
Value objects and enums Validated inputs and closed sets of options src/ValueObjects/Repo.php, src/Enums/MergeMethod.php

Key design choices

1. One connector owns every cross-cutting concern

GithubConnector sets the base URL, the X-GitHub-Api-Version header, and authentication. Its getRequestException() maps status codes to specific exceptions with a match: 401 to AuthenticationException, 403 to a rate-limit or permission error, 404 to ResourceNotFoundException, 422 to ValidationException with field-level details, 429 to RateLimitException, and 5xx to NetworkException. Calling code catches a meaningful type instead of checking status codes.

2. One request class per endpoint, grouped behind resources

Each endpoint is a small Saloon Request that knows its HTTP method, path, query, and how to turn the response into objects (createDtoFromResponse()). Resources such as RepoResource and PullRequestResource extend a readonly BaseResource and give callers methods like Github::pullRequests()->merge(...). Adding an endpoint means adding one request class and one resource method.

3. Typed responses and validated inputs

Responses map onto classes in src/Data with typed properties and Carbon dates, so IDEs and static analysis know what comes back. Inputs are validated at the edge: Repo::fromFullName('owner/repo') rejects malformed names before any HTTP call, and backed enums (MergeMethod, Visibility, Direction, Pulls\State) replace free-form strings for options.

4. Pluggable authentication

AuthenticationStrategy is a small interface (getAuthorizationHeader, validate, needsRefresh, refresh). The connector accepts either a token string or a strategy. GitHubAppAuthentication signs an RS256 JWT with the App private key, exchanges it for an installation token, and refreshes that token before it expires. With no explicit credentials, TokenResolver tries config('github-client.token') (normally GITHUB_TOKEN), then config('github-client.gh_token') (normally GH_TOKEN), then gh auth token, and falls back to unauthenticated access for public data.

$github = Github::forInstallation($installationId);
$github->pullRequests()->all('owner', 'repo');

5. Native Laravel integration

GithubClientServiceProvider uses spatie/laravel-package-tools for the config file and an Artisan command, and registers the connector as a singleton in the container. The package is auto-discovered, and the Github facade carries @method annotations so IDEs autocomplete every resource. The same client can be injected by type for code that avoids facades.

6. Tests and CI

  • Pest tests use Saloon's MockClient; unmocked Saloon network requests are blocked, and tests need no GitHub credentials.
  • The Unit suite runs without Testbench; the Package suite boots Laravel. See contributor commands for setup, filtering, and profiling.
  • Process::fake() in tests/Pest.php keeps the GitHub CLI out of the test run.
  • Architecture tests in tests/ArchTest.php enforce that resources extend BaseResource, requests extend Saloon Request, and no debug calls ship.
  • GitHub Actions tests Laravel 12 on PHP 8.2/8.3/8.4 and Laravel 13 on PHP 8.3/8.4 (run-tests.yml). Laravel 11 remains accepted by Composer for legacy consumers but is outside CI after its March 12, 2026 security EOL; security-advisory blocking stays enabled.
  • PHPStan runs once on PHP 8.2 with Laravel 12 and Larastan 3, alongside Composer validation and dependency auditing. Pint is a read-only check, not an auto-commit bot. All three workflows run on every PR and pushes to master, with read-only repository permissions and cancellation of superseded runs.
  • Dependabot keeps Composer and Actions dependencies current. Minor and patch updates are set to auto-merge.

Usage

use JordanPartridge\GithubClient\Facades\Github;
use JordanPartridge\GithubClient\Enums\MergeMethod;

$repo = Github::getRepo('laravel/framework');           // RepoData
$prs  = Github::pullRequests()->all('laravel', 'framework');
Github::pullRequests()->merge('owner', 'repo', 42, mergeMethod: MergeMethod::Squash);

Where to look

Topic File
Entry point src/Github.php
HTTP, auth, error mapping src/Connectors/GithubConnector.php
Endpoint example src/Requests/Pulls/Index.php
Resource example src/Resources/PullRequestResource.php
Typed response src/Data/Repos/RepoData.php
Input validation src/ValueObjects/Repo.php
GitHub App auth src/Auth/GitHubAppAuthentication.php
Container wiring src/GithubClientServiceProvider.php
Test conventions tests/Pest.php, tests/ArchTest.php
CI matrix .github/workflows/run-tests.yml