diff --git a/app/Livewire/Destination/Index.php b/app/Livewire/Destination/Index.php index a3df3fd56..7a4b89fab 100644 --- a/app/Livewire/Destination/Index.php +++ b/app/Livewire/Destination/Index.php @@ -3,6 +3,7 @@ namespace App\Livewire\Destination; use App\Models\Server; +use Illuminate\Support\Collection; use Livewire\Attributes\Locked; use Livewire\Component; @@ -11,9 +12,15 @@ class Index extends Component #[Locked] public $servers; - public function mount() + #[Locked] + public Collection $destinations; + + public function mount(): void { $this->servers = Server::isUsable()->get(); + $this->destinations = $this->servers + ->flatMap(fn (Server $server) => $server->standaloneDockers->concat($server->swarmDockers)) + ->values(); } public function render() diff --git a/app/Livewire/Destination/New/Docker.php b/app/Livewire/Destination/New/Docker.php index 6f9b6f995..254823163 100644 --- a/app/Livewire/Destination/New/Docker.php +++ b/app/Livewire/Destination/New/Docker.php @@ -33,44 +33,49 @@ class Docker extends Component #[Validate(['required', 'boolean'])] public bool $isSwarm = false; - public function mount(?string $server_id = null) + public function mount(?string $server_id = null): void { - $this->network = new Cuid2; + $this->network = (string) new Cuid2; $this->servers = Server::isUsable()->get(); - if ($server_id) { - $foundServer = $this->servers->find($server_id) ?: $this->servers->first(); - if (! $foundServer) { - throw new \Exception('Server not found.'); + + if (filled($server_id)) { + $this->selectedServer = Server::ownedByCurrentTeam()->whereKey($server_id)->firstOrFail(); + + if (! $this->servers->contains('id', $this->selectedServer->id)) { + $this->servers->push($this->selectedServer); } - $this->selectedServer = $foundServer; - $this->serverId = $this->selectedServer->id; + + $this->serverId = (string) $this->selectedServer->id; } else { $foundServer = $this->servers->first(); if (! $foundServer) { throw new \Exception('Server not found.'); } $this->selectedServer = $foundServer; - $this->serverId = $this->selectedServer->id; + $this->serverId = (string) $this->selectedServer->id; } $this->generateName(); } - public function updatedServerId() + public function updatedServerId(): void { $this->selectedServer = $this->servers->find($this->serverId); + if (! $this->selectedServer) { + throw new \Exception('Server not found.'); + } $this->generateName(); } - public function generateName() + public function generateName(): void { $name = data_get($this->selectedServer, 'name', new Cuid2); $this->name = str("{$name}-{$this->network}")->kebab(); } - public function submit() + public function submit(): mixed { try { - $this->authorize('create', StandaloneDocker::class); + $this->authorize('create', $this->isSwarm ? SwarmDocker::class : StandaloneDocker::class); $this->validate(); if ($this->isSwarm) { $found = $this->selectedServer->swarmDockers()->where('network', $this->network)->first(); diff --git a/app/Livewire/Server/Destinations.php b/app/Livewire/Server/Destinations.php index 117b43ad6..f3f142646 100644 --- a/app/Livewire/Server/Destinations.php +++ b/app/Livewire/Server/Destinations.php @@ -45,7 +45,7 @@ public function add($name) } else { SwarmDocker::create([ 'name' => $this->server->name.'-'.$name, - 'network' => $this->name, + 'network' => $name, 'server_id' => $this->server->id, ]); } diff --git a/resources/views/livewire/destination/index.blade.php b/resources/views/livewire/destination/index.blade.php index aecd58d7a..d5026a651 100644 --- a/resources/views/livewire/destination/index.blade.php +++ b/resources/views/livewire/destination/index.blade.php @@ -14,34 +14,30 @@
Network endpoints to deploy your resources.
- @forelse ($servers as $server) - @forelse ($server->destinations() as $destination) - @if ($destination->getMorphClass() === 'App\Models\StandaloneDocker') - -
-
{{ $destination->name }}
-
Server: {{ $destination->server->name }}
+ @forelse ($destinations as $destination) + @if ($destination->getMorphClass() === 'App\Models\StandaloneDocker') +
+
+
{{ $destination->name }}
+
Server: {{ $destination->server->name }}
+
+
+ @endif + @if ($destination->getMorphClass() === 'App\Models\SwarmDocker') + +
+
+ {{ $destination->name }} +
-
- @endif - @if ($destination->getMorphClass() === 'App\Models\SwarmDocker') - -
-
- {{ $destination->name }} - -
-
server: {{ $destination->server->name }}
-
-
- @endif - @empty -
No destinations found.
- @endforelse +
server: {{ $destination->server->name }}
+
+ + @endif @empty -
No servers found.
+
No destinations found.
@endforelse
diff --git a/resources/views/livewire/server/destinations.blade.php b/resources/views/livewire/server/destinations.blade.php index b5e8111e9..9d8a2b437 100644 --- a/resources/views/livewire/server/destinations.blade.php +++ b/resources/views/livewire/server/destinations.blade.php @@ -29,6 +29,9 @@ {{ data_get($docker, 'network') }} @endforeach + @if ($server->standaloneDockers->isEmpty() && $server->swarmDockers->isEmpty()) +
No destinations configured for this server yet.
+ @endif @if ($networks->count() > 0)
diff --git a/tests/Feature/ServerDestinationsPageTest.php b/tests/Feature/ServerDestinationsPageTest.php new file mode 100644 index 000000000..3a1fb1790 --- /dev/null +++ b/tests/Feature/ServerDestinationsPageTest.php @@ -0,0 +1,107 @@ + InstanceSettings::query()->create(['id' => 0])); + + $this->user = User::factory()->create(); + $this->team = Team::factory()->create(); + $this->user->teams()->attach($this->team, ['role' => 'owner']); + + $this->actingAs($this->user); + session(['currentTeam' => $this->team]); +}); + +test('destination creation modal can mount with selected team server even when global usable server list excludes it', function () { + $server = Server::factory()->create(['team_id' => $this->team->id]); + $server->settings()->update([ + 'is_reachable' => true, + 'is_usable' => true, + 'is_build_server' => true, + ]); + + StandaloneDocker::withoutEvents(fn () => $server->standaloneDockers()->delete()); + + Livewire::test(Docker::class, ['server_id' => (string) $server->id]) + ->assertSet('selectedServer.id', $server->id) + ->assertSet('serverId', (string) $server->id); +}); + +test('server destinations page renders when selected server has no destinations', function () { + $server = Server::factory()->create(['team_id' => $this->team->id]); + $server->settings()->update([ + 'is_reachable' => true, + 'is_usable' => true, + 'is_build_server' => true, + ]); + + StandaloneDocker::withoutEvents(fn () => $server->standaloneDockers()->delete()); + + $this->get(route('server.destinations', ['server_uuid' => $server->uuid])) + ->assertSuccessful() + ->assertSee('Destinations') + ->assertSee('No destinations configured for this server yet.') + ->assertDontSee('Server not found.'); +}); + +test('global destinations page does not render per-server empty states beside existing destinations', function () { + $serverWithDestination = Server::factory()->create(['team_id' => $this->team->id]); + $serverWithDestination->settings()->update([ + 'is_reachable' => true, + 'is_usable' => true, + ]); + + $serverWithoutDestination = Server::factory()->create(['team_id' => $this->team->id]); + $serverWithoutDestination->settings()->update([ + 'is_reachable' => true, + 'is_usable' => true, + ]); + StandaloneDocker::withoutEvents(fn () => $serverWithoutDestination->standaloneDockers()->delete()); + + $this->get(route('destination.index')) + ->assertSuccessful() + ->assertSee($serverWithDestination->standaloneDockers()->first()->name) + ->assertDontSee('No destinations found.'); +}); + +test('global destinations page renders a single empty state when no usable servers have destinations', function () { + $server = Server::factory()->create(['team_id' => $this->team->id]); + $server->settings()->update([ + 'is_reachable' => true, + 'is_usable' => true, + ]); + StandaloneDocker::withoutEvents(fn () => $server->standaloneDockers()->delete()); + + $this->get(route('destination.index')) + ->assertSuccessful() + ->assertSee('No destinations found.'); +}); + +test('adding a discovered swarm destination stores the selected network name', function () { + $server = Server::factory()->create(['team_id' => $this->team->id]); + $server->settings()->update([ + 'is_reachable' => true, + 'is_usable' => true, + 'is_swarm_manager' => true, + ]); + + Livewire::test(Destinations::class, ['server_uuid' => $server->uuid]) + ->call('add', 'customer-network'); + + expect(SwarmDocker::where('server_id', $server->id)->where('network', 'customer-network')->exists())->toBeTrue(); +});