195 lines
6.1 KiB
PHP
195 lines
6.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace KbAntoraImporter\GitLab;
|
|
|
|
final class GitLabClient
|
|
{
|
|
private string $baseUrl;
|
|
private string $token;
|
|
private string $branchPattern;
|
|
|
|
public function __construct(array $settings)
|
|
{
|
|
$this->baseUrl = self::normalizeBaseUrl((string) ($settings['gitlab_base_url'] ?? ''));
|
|
$this->token = (string) ($settings['gitlab_token'] ?? '');
|
|
$this->branchPattern = (string) ($settings['branch_pattern'] ?? '^v.*');
|
|
}
|
|
|
|
public static function normalizeBaseUrl(string $baseUrl): string
|
|
{
|
|
$baseUrl = rtrim(trim($baseUrl), '/');
|
|
|
|
if (preg_match('#/api/v4$#i', $baseUrl)) {
|
|
$baseUrl = (string) preg_replace('#/api/v4$#i', '', $baseUrl);
|
|
}
|
|
|
|
return $baseUrl;
|
|
}
|
|
|
|
public function getGroup(string $group): array|\WP_Error
|
|
{
|
|
return $this->request('GET', '/groups/' . rawurlencode($group));
|
|
}
|
|
|
|
public function getProjects(string $group): array|\WP_Error
|
|
{
|
|
return $this->requestAll('/groups/' . rawurlencode($group) . '/projects', [
|
|
'include_subgroups' => 'true',
|
|
'simple' => 'true',
|
|
'order_by' => 'path',
|
|
'sort' => 'asc',
|
|
]);
|
|
}
|
|
|
|
public function getProject(string $projectId): array|\WP_Error
|
|
{
|
|
return $this->request('GET', '/projects/' . rawurlencode($projectId));
|
|
}
|
|
|
|
public function getBranches(string $projectId): array|\WP_Error
|
|
{
|
|
return $this->requestAll('/projects/' . rawurlencode($projectId) . '/repository/branches', []);
|
|
}
|
|
|
|
public function getDocumentationBranches(string $projectId): array|\WP_Error
|
|
{
|
|
$branches = $this->getBranches($projectId);
|
|
|
|
if (is_wp_error($branches)) {
|
|
return $branches;
|
|
}
|
|
|
|
$pattern = '/' . str_replace('/', '\/', $this->branchPattern) . '/';
|
|
|
|
return array_values(array_filter($branches, static function (array $branch) use ($pattern): bool {
|
|
return isset($branch['name']) && @preg_match($pattern, (string) $branch['name']);
|
|
}));
|
|
}
|
|
|
|
public function getFileRaw(string $projectId, string $path, string $ref): string|\WP_Error
|
|
{
|
|
$response = $this->rawRequest('/projects/' . rawurlencode($projectId) . '/repository/files/' . rawurlencode($path) . '/raw', [
|
|
'ref' => $ref,
|
|
]);
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
return wp_remote_retrieve_body($response);
|
|
}
|
|
|
|
public function getTree(string $projectId, string $ref, string $path = '', bool $recursive = true): array|\WP_Error
|
|
{
|
|
return $this->requestAll('/projects/' . rawurlencode($projectId) . '/repository/tree', [
|
|
'ref' => $ref,
|
|
'path' => $path,
|
|
'recursive' => $recursive ? 'true' : 'false',
|
|
]);
|
|
}
|
|
|
|
private function requestAll(string $endpoint, array $query): array|\WP_Error
|
|
{
|
|
$page = 1;
|
|
$items = [];
|
|
|
|
do {
|
|
$response = $this->rawRequest($endpoint, array_merge($query, [
|
|
'per_page' => '100',
|
|
'page' => (string) $page,
|
|
]));
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
$decoded = json_decode(wp_remote_retrieve_body($response), true);
|
|
|
|
if (! is_array($decoded)) {
|
|
return new \WP_Error('kb_gitlab_invalid_json', __('GitLab returned invalid JSON.', 'kb-antora-importer'));
|
|
}
|
|
|
|
$items = array_merge($items, $decoded);
|
|
$next = wp_remote_retrieve_header($response, 'x-next-page');
|
|
$page = $next ? (int) $next : 0;
|
|
} while ($page > 0);
|
|
|
|
return $items;
|
|
}
|
|
|
|
private function request(string $method, string $endpoint, array $query = []): array|\WP_Error
|
|
{
|
|
$response = $this->rawRequest($endpoint, $query, $method);
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
$decoded = json_decode(wp_remote_retrieve_body($response), true);
|
|
|
|
if (! is_array($decoded)) {
|
|
return new \WP_Error('kb_gitlab_invalid_json', __('GitLab returned invalid JSON.', 'kb-antora-importer'));
|
|
}
|
|
|
|
return $decoded;
|
|
}
|
|
|
|
private function rawRequest(string $endpoint, array $query = [], string $method = 'GET'): array|\WP_Error
|
|
{
|
|
if (! $this->baseUrl || ! $this->token) {
|
|
return new \WP_Error('kb_gitlab_missing_settings', __('GitLab base URL or token is missing.', 'kb-antora-importer'));
|
|
}
|
|
|
|
$url = $this->baseUrl . '/api/v4' . $endpoint;
|
|
|
|
if ($query) {
|
|
$url = add_query_arg($query, $url);
|
|
}
|
|
|
|
$response = wp_remote_request($url, [
|
|
'method' => $method,
|
|
'timeout' => 30,
|
|
'headers' => [
|
|
'PRIVATE-TOKEN' => $this->token,
|
|
'Accept' => 'application/json',
|
|
],
|
|
]);
|
|
|
|
if (is_wp_error($response)) {
|
|
return $response;
|
|
}
|
|
|
|
$code = (int) wp_remote_retrieve_response_code($response);
|
|
|
|
if ($code >= 200 && $code < 300) {
|
|
return $response;
|
|
}
|
|
|
|
$body = wp_strip_all_tags(wp_remote_retrieve_body($response));
|
|
$body = trim(preg_replace('/\s+/', ' ', $body) ?? $body);
|
|
$body = substr($body, 0, 300);
|
|
$retryAfter = wp_remote_retrieve_header($response, 'retry-after');
|
|
$message = sprintf(
|
|
/* translators: %d is an HTTP status code. */
|
|
__('GitLab API request failed with HTTP %d.', 'kb-antora-importer'),
|
|
$code
|
|
);
|
|
|
|
if (503 === $code) {
|
|
$message .= ' ' . __('The GitLab server or a proxy returned Service Unavailable. Check whether GitLab is reachable from the WordPress server and whether the Base URL points to the GitLab root, not to /api/v4.', 'kb-antora-importer');
|
|
}
|
|
|
|
return new \WP_Error(
|
|
'kb_gitlab_http_' . $code,
|
|
$message,
|
|
[
|
|
'status' => $code,
|
|
'url' => esc_url_raw($url),
|
|
'retry_after' => $retryAfter ? (string) $retryAfter : '',
|
|
'response_excerpt' => $body,
|
|
]
|
|
);
|
|
}
|
|
}
|