username !== '' && $this->token !== ''; } public function ping(): void { $this->client->request('GET', 'https://api.github.com/user', [ 'headers' => ['Authorization' => "Bearer {$this->token}"], ])->getContent(); } /** Fires the GraphQL contributionsCollection query bounded by $since/$until. */ public function startFetch(?\DateTimeImmutable $since = null, ?\DateTimeImmutable $until = null): ResponseInterface { $this->logger->debug('GitHubProvider: fetching contributions', ['user' => $this->username]); $from = ($since ?? new \DateTimeImmutable('-365 days'))->format('Y-m-d\T00:00:00\Z'); $to = ($until ?? new \DateTimeImmutable())->format('Y-m-d\T23:59:59\Z'); $query = sprintf( 'query { user(login: %s) { contributionsCollection(from: %s, to: %s) { contributionCalendar { weeks { contributionDays { date contributionCount } } } } } }', json_encode($this->username), json_encode($from), json_encode($to), ); // GitHub's GraphQL API requires application/json. // request() doesn't block; the response is read in resolveFetch() so // multiple providers' requests can be in flight at once. return $this->client->request('POST', self::GRAPHQL_URL, [ 'headers' => [ 'Authorization' => "Bearer {$this->token}", 'Content-Type' => 'application/json', ], 'json' => ['query' => $query], ]); } /** * Parses the GraphQL response into a per-day count map, skipping zero-count days. * * @param ResponseInterface $handle the response returned by startFetch() * @return array date (Y-m-d) => contribution count */ public function resolveFetch(mixed $handle): array { /** @var ResponseInterface $handle */ $data = $handle->toArray(); if (isset($data['errors'])) { throw new ServiceUnavailableHttpException(null, 'GitHub GraphQL error: ' . json_encode($data['errors'])); } $result = []; $weeks = $data['data']['user']['contributionsCollection']['contributionCalendar']['weeks'] ?? []; foreach ($weeks as $week) { foreach ($week['contributionDays'] as $day) { if ($day['contributionCount'] > 0) { $result[$day['date']] = $day['contributionCount']; } } } $this->logger->info('GitHubProvider: fetched contributions', [ 'user' => $this->username, 'days' => count($result), 'total' => array_sum($result), ]); return $result; } }