diff --git a/src/VCS/Adapter.php b/src/VCS/Adapter.php index 27478a95..6986178d 100644 --- a/src/VCS/Adapter.php +++ b/src/VCS/Adapter.php @@ -80,9 +80,14 @@ abstract public function getUser(string $username): array; /** * Get owner name of the installation * - * @return string + * For GitHub: Uses installationId to identify the GitHub App installation + * For Gitea: Requires repositoryId since OAuth tokens can access multiple organizations + * + * @param string $installationId Installation ID (GitHub) or empty string (Gitea) + * @param int|null $repositoryId Repository ID (required for Gitea, ignored by GitHub) + * @return string Owner login/username */ - abstract public function getOwnerName(string $installationId): string; + abstract public function getOwnerName(string $installationId, ?int $repositoryId = null): string; /** * Search repositories for GitHub App diff --git a/src/VCS/Adapter/Git/GitHub.php b/src/VCS/Adapter/Git/GitHub.php index b57290d1..958cac09 100644 --- a/src/VCS/Adapter/Git/GitHub.php +++ b/src/VCS/Adapter/Git/GitHub.php @@ -618,10 +618,13 @@ public function getUser(string $username): array /** * Get owner name of the GitHub installation * - * @return string + * @param string $installationId GitHub App installation ID + * @param int|null $repositoryId Not used by GitHub (parameter exists for Gitea compatibility) + * @return string Owner login/username */ - public function getOwnerName(string $installationId): string + public function getOwnerName(string $installationId, ?int $repositoryId = null): string { + // GitHub doesn't use $repositoryId - only installationId $url = '/app/installations/' . $installationId; $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->jwtToken"]); diff --git a/src/VCS/Adapter/Git/Gitea.php b/src/VCS/Adapter/Git/Gitea.php index c7c3fd10..aedf6c7e 100644 --- a/src/VCS/Adapter/Git/Gitea.php +++ b/src/VCS/Adapter/Git/Gitea.php @@ -531,9 +531,35 @@ public function getUser(string $username): array throw new Exception("Not implemented yet"); } - public function getOwnerName(string $installationId): string + public function getOwnerName(string $installationId, ?int $repositoryId = null): string { - throw new Exception("getOwnerName() is not applicable for Gitea"); + if ($repositoryId === null || $repositoryId <= 0) { + throw new Exception("repositoryId is required for Gitea"); + } + + $url = "/repositories/{$repositoryId}"; + + $response = $this->call(self::METHOD_GET, $url, ['Authorization' => "token $this->accessToken"]); + + $responseHeaders = $response['headers'] ?? []; + $responseHeadersStatusCode = $responseHeaders['status-code'] ?? 0; + + if ($responseHeadersStatusCode === 404) { + throw new RepositoryNotFound("Repository not found"); + } + + if ($responseHeadersStatusCode >= 400) { + throw new Exception("Failed to get repository: HTTP {$responseHeadersStatusCode}"); + } + + $responseBody = $response['body'] ?? []; + $owner = $responseBody['owner'] ?? []; + + if (empty($owner['login'])) { + throw new Exception("Owner login missing or empty in response"); + } + + return $owner['login']; } public function getPullRequest(string $owner, string $repositoryName, int $pullRequestNumber): array diff --git a/tests/VCS/Adapter/GiteaTest.php b/tests/VCS/Adapter/GiteaTest.php index 4405bab3..bed2ff4c 100644 --- a/tests/VCS/Adapter/GiteaTest.php +++ b/tests/VCS/Adapter/GiteaTest.php @@ -721,19 +721,53 @@ public function testDeleteNonExistingRepositoryFails(): void } public function testGetOwnerName(): void + { + $repositoryName = 'test-get-owner-name-' . \uniqid(); + $created = $this->vcsAdapter->createRepository(self::$owner, $repositoryName, false); + + try { + $this->assertIsArray($created); + $this->assertArrayHasKey('id', $created); + $this->assertIsScalar($created['id']); + $repositoryId = (int) $created['id']; + + $ownerName = $this->vcsAdapter->getOwnerName('', $repositoryId); + + $this->assertSame(self::$owner, $ownerName); + } finally { + $this->vcsAdapter->deleteRepository(self::$owner, $repositoryName); + } + } + + public function testGetOwnerNameWithZeroRepositoryId(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('repositoryId is required for Gitea'); + + $this->vcsAdapter->getOwnerName('', 0); + } + + public function testGetOwnerNameWithoutRepositoryId(): void { $this->expectException(\Exception::class); - $this->expectExceptionMessage('not applicable for Gitea'); + $this->expectExceptionMessage('repositoryId is required for Gitea'); $this->vcsAdapter->getOwnerName(''); } - public function testGetOwnerNameWithRandomInput(): void + public function testGetOwnerNameWithInvalidRepositoryId(): void + { + $this->expectException(\Utopia\VCS\Exception\RepositoryNotFound::class); + + $this->vcsAdapter->getOwnerName('', 999999999); + } + + public function testGetOwnerNameWithNullRepositoryId(): void { $this->expectException(\Exception::class); - $this->expectExceptionMessage('not applicable for Gitea'); + $this->expectExceptionMessage('repositoryId is required for Gitea'); - $this->vcsAdapter->getOwnerName('random-gibberish-' . \uniqid()); + $this->vcsAdapter->getOwnerName('', null); } public function testGetPullRequestFromBranch(): void