baseUrl = rtrim($baseUrl !== '' ? $baseUrl : 'https://gitlab.com', '/'); } public function getName(): string { return 'gitlab'; } public function isConfigured(): bool { return $this->username !== '' && $this->token !== ''; } public function ping(): void { $this->client->request('GET', "$this->baseUrl/api/v4/user", [ 'headers' => ['PRIVATE-TOKEN' => $this->token], ])->getContent(); } /** * Resolves the username to a numeric user ID, then fires the first page of the events request. * * @return array{baseUrl: string, userId: int, after: string, before: ?string, page: int, response: ResponseInterface} * * ponytail: the user-id lookup and event pagination stay sequential within * this one provider (each page depends on the previous). Parallelism here * only spans across providers; revisit only if GitLab pagination itself * becomes the bottleneck. */ public function startFetch(?\DateTimeImmutable $since = null, ?\DateTimeImmutable $until = null): array { $this->logger->debug('GitLabProvider: fetching contributions', ['user' => $this->username, 'url' => $this->baseUrl]); $userResponse = $this->client->request('GET', "$this->baseUrl/api/v4/users", [ 'headers' => ['PRIVATE-TOKEN' => $this->token], 'query' => ['username' => $this->username], ]); $users = $userResponse->toArray(); if (empty($users)) { throw new NotFoundHttpException("GitLab: user '{$this->username}' not found on $this->baseUrl"); } $userId = $users[0]['id']; $after = ($since ?? new \DateTimeImmutable('-365 days'))->format('Y-m-d'); $before = $until?->format('Y-m-d'); $query = ['after' => $after, 'per_page' => 100, 'page' => 1]; if ($before !== null) { $query['before'] = $before; } $response = $this->client->request('GET', "$this->baseUrl/api/v4/users/$userId/events", [ 'headers' => ['PRIVATE-TOKEN' => $this->token], 'query' => $query, ]); return ['baseUrl' => $this->baseUrl, 'userId' => $userId, 'after' => $after, 'before' => $before, 'page' => 1, 'response' => $response]; } /** * Follows pagination (100 events/page) until a short page is returned, counting events per day. * * @param array{baseUrl: string, userId: int, after: string, before: ?string, page: int, response: ResponseInterface} $handle * @return array date (Y-m-d) => event count */ public function resolveFetch(mixed $handle): array { ['baseUrl' => $baseUrl, 'userId' => $userId, 'after' => $after, 'before' => $before, 'page' => $page, 'response' => $response] = $handle; $result = []; do { $events = $response->toArray(); foreach ($events as $event) { $date = substr($event['created_at'], 0, 10); $result[$date] = ($result[$date] ?? 0) + 1; } $page++; if (count($events) === 100) { $query = ['after' => $after, 'per_page' => 100, 'page' => $page]; if ($before !== null) { $query['before'] = $before; } $response = $this->client->request('GET', "$baseUrl/api/v4/users/$userId/events", [ 'headers' => ['PRIVATE-TOKEN' => $this->token], 'query' => $query, ]); } } while (count($events) === 100); $this->logger->info('GitLabProvider: fetched contributions', [ 'user' => $this->username, 'pages' => $page - 1, 'days' => count($result), 'total' => array_sum($result), ]); return $result; } }