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.
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.
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.
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.
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>
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>
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>
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>
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>
- 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>
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>