fix(github): preserve custom app API URLs
This commit is contained in:
parent
a7dddccd2e
commit
1adeed2a48
6 changed files with 101 additions and 25 deletions
|
|
@ -256,7 +256,9 @@ public function create_github_app(Request $request)
|
|||
'uuid' => Str::uuid(),
|
||||
'name' => $request->input('name'),
|
||||
'organization' => $request->input('organization'),
|
||||
'api_url' => githubApiUrlFromHtmlUrl($request->input('html_url')),
|
||||
'api_url' => filled($request->input('api_url'))
|
||||
? $request->input('api_url')
|
||||
: githubApiUrlFromHtmlUrl($request->input('html_url')),
|
||||
'html_url' => $request->input('html_url'),
|
||||
'custom_user' => $request->input('custom_user', 'git'),
|
||||
'custom_port' => $request->input('custom_port', 22),
|
||||
|
|
@ -650,7 +652,7 @@ public function update_github_app(Request $request, $github_app_id)
|
|||
if (array_key_exists('organization', $payload)) {
|
||||
$payload['organization'] = normalizeGithubOrganization($payload['organization']);
|
||||
}
|
||||
if (isset($payload['html_url'])) {
|
||||
if (isset($payload['html_url']) && ! filled($payload['api_url'] ?? null)) {
|
||||
$payload['api_url'] = githubApiUrlFromHtmlUrl($payload['html_url']);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ class Change extends Component
|
|||
|
||||
public string $activeTab = 'general';
|
||||
|
||||
private bool $shouldDeriveApiUrlAfterHtmlUrlUpdate = false;
|
||||
|
||||
protected function rules(): array
|
||||
{
|
||||
return [
|
||||
|
|
@ -104,9 +106,17 @@ protected function rules(): array
|
|||
];
|
||||
}
|
||||
|
||||
public function updatingHtmlUrl(): void
|
||||
{
|
||||
$this->shouldDeriveApiUrlAfterHtmlUrlUpdate = blank($this->apiUrl)
|
||||
|| $this->apiUrl === githubApiUrlFromHtmlUrl($this->htmlUrl);
|
||||
}
|
||||
|
||||
public function updatedHtmlUrl(): void
|
||||
{
|
||||
$this->apiUrl = githubApiUrlFromHtmlUrl($this->htmlUrl);
|
||||
if ($this->shouldDeriveApiUrlAfterHtmlUrlUpdate) {
|
||||
$this->apiUrl = githubApiUrlFromHtmlUrl($this->htmlUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public function boot()
|
||||
|
|
@ -126,7 +136,9 @@ private function syncData(bool $toModel = false): void
|
|||
if ($toModel) {
|
||||
// Sync TO model (before save)
|
||||
$this->organization = normalizeGithubOrganization($this->organization);
|
||||
$this->apiUrl = githubApiUrlFromHtmlUrl($this->htmlUrl);
|
||||
$this->apiUrl = filled($this->apiUrl)
|
||||
? $this->apiUrl
|
||||
: githubApiUrlFromHtmlUrl($this->htmlUrl);
|
||||
|
||||
$this->github_app->name = $this->name;
|
||||
$this->github_app->organization = $this->organization;
|
||||
|
|
@ -354,7 +366,9 @@ public function submit()
|
|||
|
||||
$this->github_app->makeVisible('client_secret')->makeVisible('webhook_secret');
|
||||
$this->organization = normalizeGithubOrganization($this->organization);
|
||||
$this->apiUrl = githubApiUrlFromHtmlUrl($this->htmlUrl);
|
||||
$this->apiUrl = filled($this->apiUrl)
|
||||
? $this->apiUrl
|
||||
: githubApiUrlFromHtmlUrl($this->htmlUrl);
|
||||
$this->validate();
|
||||
|
||||
$this->syncData(true);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
use App\Models\GithubApp;
|
||||
use App\Rules\SafeExternalUrl;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Livewire\Component;
|
||||
|
||||
class Create extends Component
|
||||
|
|
@ -25,14 +26,24 @@ class Create extends Component
|
|||
|
||||
public bool $is_system_wide = false;
|
||||
|
||||
private bool $shouldDeriveApiUrlAfterHtmlUrlUpdate = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->name = substr(generate_random_name(), 0, 30);
|
||||
}
|
||||
|
||||
public function updatingHtmlUrl(): void
|
||||
{
|
||||
$this->shouldDeriveApiUrlAfterHtmlUrlUpdate = blank($this->api_url)
|
||||
|| $this->api_url === githubApiUrlFromHtmlUrl($this->html_url);
|
||||
}
|
||||
|
||||
public function updatedHtmlUrl(): void
|
||||
{
|
||||
$this->api_url = githubApiUrlFromHtmlUrl($this->html_url);
|
||||
if ($this->shouldDeriveApiUrlAfterHtmlUrlUpdate) {
|
||||
$this->api_url = githubApiUrlFromHtmlUrl($this->html_url);
|
||||
}
|
||||
}
|
||||
|
||||
public function createGitHubApp()
|
||||
|
|
@ -41,7 +52,9 @@ public function createGitHubApp()
|
|||
$this->authorize('createAnyResource');
|
||||
|
||||
$this->organization = normalizeGithubOrganization($this->organization);
|
||||
$this->api_url = githubApiUrlFromHtmlUrl($this->html_url);
|
||||
$this->api_url = filled($this->api_url)
|
||||
? $this->api_url
|
||||
: githubApiUrlFromHtmlUrl($this->html_url);
|
||||
|
||||
$this->validate([
|
||||
'name' => 'required|string',
|
||||
|
|
@ -68,6 +81,8 @@ public function createGitHubApp()
|
|||
}
|
||||
|
||||
return redirectRoute($this, 'source.github.show', ['github_app_uuid' => $github_app->uuid]);
|
||||
} catch (ValidationException $e) {
|
||||
throw $e;
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
|
|
@ -10,19 +11,22 @@
|
|||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
InstanceSettings::forceCreate(['id' => 0, 'is_api_enabled' => true]);
|
||||
|
||||
// Create a team with owner
|
||||
$this->team = Team::factory()->create();
|
||||
$this->user = User::factory()->create();
|
||||
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
// Create an API token for the user
|
||||
$this->token = $this->user->createToken('test-token', ['*'], $this->team->id);
|
||||
$this->token = $this->user->createToken('test-token');
|
||||
$this->bearerToken = $this->token->plainTextToken;
|
||||
|
||||
// Create a private key for the team
|
||||
$this->privateKey = PrivateKey::create([
|
||||
'name' => 'Test Key',
|
||||
'private_key' => 'test-private-key-content',
|
||||
'private_key' => validGithubAppsApiPrivateKey(),
|
||||
'team_id' => $this->team->id,
|
||||
]);
|
||||
});
|
||||
|
|
@ -139,7 +143,8 @@ function validGithubAppsApiPrivateKey(): string
|
|||
$otherTeam = Team::factory()->create();
|
||||
$otherUser = User::factory()->create();
|
||||
$otherTeam->members()->attach($otherUser->id, ['role' => 'owner']);
|
||||
$otherToken = $otherUser->createToken('other-token', ['*'], $otherTeam->id);
|
||||
session(['currentTeam' => $otherTeam]);
|
||||
$otherToken = $otherUser->createToken('other-token');
|
||||
|
||||
// System-wide apps should be visible to other teams
|
||||
$response = $this->withHeaders([
|
||||
|
|
@ -173,7 +178,7 @@ function validGithubAppsApiPrivateKey(): string
|
|||
$otherTeam = Team::factory()->create();
|
||||
$otherPrivateKey = PrivateKey::create([
|
||||
'name' => 'Other Key',
|
||||
'private_key' => 'other-key',
|
||||
'private_key' => validGithubAppsApiPrivateKey(),
|
||||
'team_id' => $otherTeam->id,
|
||||
]);
|
||||
GithubApp::create([
|
||||
|
|
@ -249,7 +254,7 @@ function validGithubAppsApiPrivateKey(): string
|
|||
])->postJson('/api/v1/github-apps', [
|
||||
'name' => 'GHE App',
|
||||
'organization' => '/octocorp/',
|
||||
'html_url' => 'https://octocorp.ghe.com',
|
||||
'html_url' => 'https://github.ghe.com',
|
||||
'app_id' => 12345,
|
||||
'installation_id' => 67890,
|
||||
'client_id' => 'test-client-id',
|
||||
|
|
@ -261,12 +266,36 @@ function validGithubAppsApiPrivateKey(): string
|
|||
$response->assertCreated()
|
||||
->assertJsonFragment([
|
||||
'organization' => 'octocorp',
|
||||
'api_url' => 'https://api.octocorp.ghe.com',
|
||||
'html_url' => 'https://octocorp.ghe.com',
|
||||
'api_url' => 'https://api.github.ghe.com',
|
||||
'html_url' => 'https://github.ghe.com',
|
||||
]);
|
||||
});
|
||||
|
||||
test('normalizes ghe dot com api url when updating github apps', function () {
|
||||
test('preserves provided api url when creating github apps', function () {
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||
])->postJson('/api/v1/github-apps', [
|
||||
'name' => 'GHE App',
|
||||
'organization' => '/octocorp/',
|
||||
'api_url' => 'https://github.com/api/v3',
|
||||
'html_url' => 'https://github.com',
|
||||
'app_id' => 12345,
|
||||
'installation_id' => 67890,
|
||||
'client_id' => 'test-client-id',
|
||||
'client_secret' => 'test-client-secret',
|
||||
'webhook_secret' => 'test-webhook-secret',
|
||||
'private_key_uuid' => $this->privateKey->uuid,
|
||||
]);
|
||||
|
||||
$response->assertCreated()
|
||||
->assertJsonFragment([
|
||||
'organization' => 'octocorp',
|
||||
'api_url' => 'https://github.com/api/v3',
|
||||
'html_url' => 'https://github.com',
|
||||
]);
|
||||
});
|
||||
|
||||
test('preserves provided api url when updating github apps', function () {
|
||||
$githubApp = GithubApp::create([
|
||||
'name' => 'GHE App',
|
||||
'api_url' => 'https://github.company.internal/api/v3',
|
||||
|
|
@ -285,12 +314,12 @@ function validGithubAppsApiPrivateKey(): string
|
|||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||
])->patchJson("/api/v1/github-apps/{$githubApp->id}", [
|
||||
'html_url' => 'https://octocorp.ghe.com',
|
||||
'api_url' => 'https://octocorp.ghe.com/api/v3',
|
||||
'html_url' => 'https://github.com',
|
||||
'api_url' => 'https://github.com/api/v3',
|
||||
]);
|
||||
|
||||
$response->assertSuccessful()
|
||||
->assertJsonPath('data.api_url', 'https://api.octocorp.ghe.com');
|
||||
->assertJsonPath('data.api_url', 'https://github.com/api/v3');
|
||||
});
|
||||
|
||||
test('rejects invalid organization when creating github apps', function () {
|
||||
|
|
|
|||
|
|
@ -419,7 +419,7 @@ function validPrivateKey(): string
|
|||
expect($githubApp->private_key_id)->toBe($privateKey->id);
|
||||
});
|
||||
|
||||
test('normalizes resolvable ghe dot com api url when saving github app settings', function () {
|
||||
test('preserves custom ghe api url when saving github app settings', function () {
|
||||
$githubApp = GithubApp::create([
|
||||
'name' => 'Test GitHub App',
|
||||
'api_url' => 'https://github.ghe.com/api/v3',
|
||||
|
|
@ -437,10 +437,10 @@ function validPrivateKey(): string
|
|||
->set('apiUrl', 'https://github.ghe.com/api/v3')
|
||||
->call('submit')
|
||||
->assertDispatched('success')
|
||||
->assertSet('apiUrl', 'https://api.github.ghe.com');
|
||||
->assertSet('apiUrl', 'https://github.ghe.com/api/v3');
|
||||
|
||||
$githubApp->refresh();
|
||||
expect($githubApp->api_url)->toBe('https://api.github.ghe.com');
|
||||
expect($githubApp->api_url)->toBe('https://github.ghe.com/api/v3');
|
||||
});
|
||||
|
||||
test('rejects invalid github organization values', function () {
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@
|
|||
Livewire::test(Create::class)
|
||||
->assertSuccessful()
|
||||
->set('name', 'enterprise-app')
|
||||
->set('api_url', 'https://github.enterprise.com/api/v3')
|
||||
->set('html_url', 'https://github.enterprise.com')
|
||||
->set('api_url', 'https://github.ghe.com/api/v3')
|
||||
->set('html_url', 'https://github.ghe.com')
|
||||
->set('custom_user', 'git-custom')
|
||||
->set('custom_port', 2222)
|
||||
->call('createGitHubApp')
|
||||
|
|
@ -82,12 +82,28 @@
|
|||
$githubApp = GithubApp::where('name', 'enterprise-app')->first();
|
||||
|
||||
expect($githubApp)->not->toBeNull();
|
||||
expect($githubApp->api_url)->toBe('https://github.enterprise.com/api/v3');
|
||||
expect($githubApp->html_url)->toBe('https://github.enterprise.com');
|
||||
expect($githubApp->api_url)->toBe('https://github.ghe.com/api/v3');
|
||||
expect($githubApp->html_url)->toBe('https://github.ghe.com');
|
||||
expect($githubApp->custom_user)->toBe('git-custom');
|
||||
expect($githubApp->custom_port)->toBe(2222);
|
||||
});
|
||||
|
||||
test('preserves custom github enterprise api url when creating github app', function () {
|
||||
Livewire::test(Create::class)
|
||||
->assertSuccessful()
|
||||
->set('name', 'ghe-custom-api-app')
|
||||
->set('api_url', 'https://github.ghe.com/api/v3')
|
||||
->set('html_url', 'https://github.ghe.com')
|
||||
->call('createGitHubApp')
|
||||
->assertRedirect();
|
||||
|
||||
$githubApp = GithubApp::where('name', 'ghe-custom-api-app')->first();
|
||||
|
||||
expect($githubApp)->not->toBeNull();
|
||||
expect($githubApp->api_url)->toBe('https://github.ghe.com/api/v3');
|
||||
expect($githubApp->html_url)->toBe('https://github.ghe.com');
|
||||
});
|
||||
|
||||
test('validates required fields', function () {
|
||||
Livewire::test(Create::class)
|
||||
->assertSuccessful()
|
||||
|
|
|
|||
Loading…
Reference in a new issue