Commit Graph
31 Commits
Author SHA1 Message Date
haylan 9c028aaf5e docs(phpdoc): document public methods across src/
Add professional PHPDoc (summary + @param/@return) to the remaining
public methods that had none or only partial coverage: GraphController's
actions, ContributionStore's accessors, SvgRenderer's render() and
private helpers (with concrete array-shape annotations), ProbeTrait,
ProviderHealthChecker, ProviderStatus, and the Contribution entity.
Also adds the matching @return shapes on SvgRendererTest's data
providers.
2026-07-12 23:43:31 +02:00
haylan 8e349c961b fix(entity): re-index ContributionCollection constructor arguments
array_map() over the variadic constructor can produce non-sequential or
string keys, which doesn't match the array<int, Contribution> property
type; re-index with array_values(). Also documents the iterator/count
accessors.
2026-07-12 23:43:01 +02:00
haylan 14ea788522 fix(renderer): coerce strtotime() result to int before formatting dates
date() expects int|null, but strtotime() returns int|false; cast to make
the type explicit for PHPStan level 8 (the date strings are always
well-formed here, so strtotime() never actually returns false).
2026-07-12 23:42:50 +02:00
haylan 4ff45b7c46 feat(store): fetch contributions incrementally and persist history in SQLite
Bound each provider's startFetch()/resolveFetch() to an optional
since/until window, wire a SQLite-backed ContributionStore into
ContributionAggregator (fetch only the trailing window past the last
stored date, merge into the store, prune by CONTRIBUTIONS_RETENTION_DAYS),
and add a graph:contributions:refetch command to force a full or ranged
re-fetch in <=365-day chunks. This is the root fix for the full
365-day-refetch timeout that used to hit on every cache miss.
2026-07-12 23:42:41 +02:00
haylan 41e88144a4 chore(namespace): rewirtten namspeace from app to GitContributionGraph. 2026-07-12 22:47:34 +02:00
haylan e7f14dfd35 chore(doc): documentet all public methods 2026-07-12 12:27:42 +02:00
haylan 9087f91855 feat(store): finish ContributionStore and add typed entities
- Fix add()'s upsert SQL (was invalid SQL from typos) and use it for
  the new merge() as well.
- Fix remove()'s missing FROM keyword and drop the unindexed LIKE scan
  in favor of an exact match.
- Add latestDate(), all() with optional sinceDays filtering, and
  prune() with a retention-day cutoff.
- Add Contribution (immutable value object) and ContributionCollection
  (IteratorAggregate + Countable) so all() returns something typed
  instead of a raw date => count array.
- Cover all of the above with unit tests against an in-memory SQLite
  database.
2026-07-11 16:46:21 +02:00
haylan 885dec357e refactor(github): build GraphQL query without the client bundle
The eightpoints/guzzle-bundle and idci/graphql-client-bundle only
existed to build one near-static query string, and the bundle's own
HTTP transport was already bypassed in favor of Symfony HttpClient.
Build the query with sprintf/json_encode instead, drop both bundles
from config/bundles.php, and update the test double accordingly.
2026-07-11 16:46:11 +02:00
haylan 78e7a72ee9 fix(provider): normalize baseUrl before assigning readonly property
Promoted readonly properties can't be reassigned in the constructor
body, so GitLabProvider and GiteaProvider threw on every instantiation.
Normalize the plain parameter first, then assign to a separately
declared readonly property.
2026-07-11 16:46:04 +02:00
haylan c929fdfda6 feat(db): prepared add and remove function 2026-07-08 20:20:42 +02:00
haylan 3d2c0487b7 feat(store): fist stepts for implement ContributionStore for managing contributions in SQLite 2026-07-08 06:50:56 +02:00
haylan 5e27a5bc93 fix(provider): dedoupet base URL is properly formatted in GitLab and Gitea providers 2026-07-08 06:50:25 +02:00
haylan cd326a34ba chore(controller): lower set_time_limit now that fetching is concurrent
Providers fetch in parallel now (cost ~= max, not sum), so the old
90s stopgap for the 3-sequential-providers-plus-pagination worst case
can come back down toward the 30s default, with headroom for
pagination and per-request HTTP timeouts.
2026-07-07 13:32:17 +02:00
haylan fcf841f7ad feat(aggregator): fetch all providers concurrently
aggregate() now fires every configured provider's startFetch() in one
pass before resolving any of them, so wall-clock cost is roughly
max(providers) instead of sum(providers). Partial-failure isolation
and per-date summation behavior are unchanged.
2026-07-07 13:31:36 +02:00
haylan 9b631f4777 refactor(gitlab): make GitLabProvider fetch non-blocking
startFetch() resolves the user id and fires the first events page,
returning a handle without reading it; resolveFetch() continues the
existing do/while pagination starting from that first response.

Pagination itself stays sequential within this provider since each
page depends on the previous one - parallelism here only spans across
providers, not within GitLab's own pages.
2026-07-07 13:31:30 +02:00
haylan 596260bb85 refactor(gitea): make GiteaProvider fetch non-blocking
startFetch() fires the heatmap GET and returns the response without
reading it; resolveFetch() does the .toArray()/parse/log work.
2026-07-07 13:31:21 +02:00
haylan 9f3c54afd1 refactor(github): make GitHubProvider fetch non-blocking
startFetch() fires the GraphQL POST and returns the response without
reading it; resolveFetch() does the .toArray()/parse/log work that
used to happen inline right after the request.
2026-07-07 13:31:15 +02:00
haylan 11bec43bbe refactor(provider): split fetch() into startFetch()/resolveFetch()
Splits the provider contract into a non-blocking startFetch() that
fires the HTTP request(s) and a resolveFetch() that reads the result,
so callers can fire every provider's request before blocking on any
of them. Symfony HttpClient is already async under the hood -
request() doesn't block until you read the response - so this needs
no new dependency.
2026-07-07 13:31:09 +02:00
haylanandClaude Sonnet 4.6 2f3268c0b7 refactor: reorganise Service/ into Provider/ and Renderer/ sub-namespaces
Move all provider-related classes, enums, interface and trait into
App\Service\Provider; move SvgRenderer into App\Service\Renderer.
ContributionAggregator stays at the Service root as the orchestrator.
Test namespaces and use statements updated to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 00:10:09 +02:00
haylanandClaude Sonnet 4.6 61b7735afc refactor(renderer): add strict_types declaration and make class final
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 14:13:51 +02:00
haylanandClaude Sonnet 4.6 c70d96c3aa feat(health): make /health report per-provider probe status
Introduce ProviderHealthChecker which probes each configured provider
via AutowireIterator. Wire it into GraphController so /health returns
detailed per-provider status and responds 503 when any provider is
in a degraded state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 14:13:24 +02:00
haylanandClaude Sonnet 4.6 d3b9463c57 feat(provider): implement ping and probe on GitHub, GitLab, and Gitea
Add getName() and ping() to each provider and wire ProbeTrait. Make
all classes final, add strict_types declarations, and replace generic
RuntimeException with typed HTTP exceptions so probe() can classify
auth failures and unreachable endpoints correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 14:13:14 +02:00
haylanandClaude Sonnet 4.6 71bfb38028 feat(provider): add health-probe contract and supporting value objects
Add getName(), ping(), and probe() to ProviderInterface. Introduce
ProbeTrait to classify HTTP/transport exceptions into typed error codes,
ProviderStatus as the result value object, and ProviderStatusType /
ProviderErrorCode as backing enums.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 14:13:07 +02:00
haylanandClaude Sonnet 4.6 c205fed14b refactor(controller): extract contribution aggregation into dedicated service
Move fetchAllContributions and merge logic from GraphController into a new
ContributionAggregator service. Replace deprecated TaggedIterator with
AutowireIterator throughout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 12:58:17 +02:00
haylan cceda8abf2 feat: integrate Monolog for enhanced logging across providers and controller 2026-05-29 18:57:25 +02:00
haylan 8b8ec9ab2f feat: add redirect from index route to graph.svg with query parameters 2026-05-29 18:28:56 +02:00
haylan 56035096c6 feat: implement multi-provider architecture for contribution fetching and add Docker support 2026-05-29 11:51:05 +02:00
haylanandClaude Sonnet 4.6 fa035781fd fix(svg-renderer): correct date alignment and drop unused parameter
- modify('Sunday') → modify('last Sunday') so the grid start is
  reliably the prior Sunday when today is already Sunday
- Remove unused $totalW from renderLegend signature
- Remove unused GAP constant (value preserved inline as a comment on STEP)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 22:09:31 +02:00
haylanandClaude Sonnet 4.6 d1dce19832 refactor(github): migrate contribution query to GraphQL bundle
Use GraphQLApiClientRegistryInterface to build the query via the
bundle's query builder. Keep Symfony HttpClient for the actual POST
since GitHub requires application/json (the bundle's transport sends
form_params instead).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 22:09:25 +02:00
haylan 3a42bec2b7 Add environment variables for GitHub, GitLab, Gitea, and allowed hosts configuration 2026-05-28 19:53:33 +02:00
haylan 342490035b Initialize git-contribution-graph project with Docker setup, environment configuration, and core functionality for merging contributions from GitHub, GitLab, and Gitea into an SVG heatmap. 2026-05-28 19:35:44 +02:00