From f9df9740696aea08ab0e0a41e50bfaafe35d8b02 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 15 Oct 2026 22:52:05 +0200 Subject: [PATCH] fix(api): accept connect_to_docker_network when creating services Closes #12014 --- .../Controllers/Api/ServicesController.php | 6 +- openapi.json | 5 ++ openapi.yaml | 4 + .../ServiceConnectToDockerNetworkApiTest.php | 78 +++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/ServiceConnectToDockerNetworkApiTest.php diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index 7411f8a472..a2a97a3446 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -308,6 +308,7 @@ class ServicesController extends Controller 'destination_uuid' => ['type' => 'string', 'description' => 'Destination UUID. Required if server has multiple destinations.'], 'instant_deploy' => ['type' => 'boolean', 'default' => false, 'description' => 'Start the service immediately after creation.'], 'docker_compose_raw' => ['type' => 'string', 'description' => 'The base64 encoded Docker Compose content.'], + 'connect_to_docker_network' => ['type' => 'boolean', 'default' => false, 'description' => 'Connect the service to the predefined docker network.'], 'urls' => [ 'type' => 'array', 'description' => 'Array of URLs to be applied to containers of a service.', @@ -388,7 +389,7 @@ class ServicesController extends Controller )] public function create_service(Request $request) { - $allowedFields = ['type', 'name', 'description', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'docker_compose_raw', 'urls', 'force_domain_override', 'is_container_label_escape_enabled', 'tags']; + $allowedFields = ['type', 'name', 'description', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'docker_compose_raw', 'connect_to_docker_network', 'urls', 'force_domain_override', 'is_container_label_escape_enabled', 'tags']; $teamId = getTeamIdFromToken(); if (is_null($teamId)) { @@ -416,6 +417,7 @@ class ServicesController extends Controller 'name' => 'string|max:255', 'description' => 'string|nullable', 'instant_deploy' => 'boolean', + 'connect_to_docker_network' => 'boolean', 'urls' => 'array|nullable', 'urls.*' => 'array:name,url', 'urls.*.name' => 'string|required', @@ -544,7 +546,7 @@ class ServicesController extends Controller 'destination_id' => $destination->id, 'destination_type' => $destination->getMorphClass(), ]; - if (in_array($oneClickServiceName, NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK)) { + if (in_array($oneClickServiceName, NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK) || $request->boolean('connect_to_docker_network')) { data_set($servicePayload, 'connect_to_docker_network', true); } $service = new Service($servicePayload); diff --git a/openapi.json b/openapi.json index 37a8275d34..ec08efd153 100644 --- a/openapi.json +++ b/openapi.json @@ -19239,6 +19239,11 @@ "type": "string", "description": "The base64 encoded Docker Compose content." }, + "connect_to_docker_network": { + "type": "boolean", + "default": false, + "description": "Connect the service to the predefined docker network." + }, "urls": { "type": "array", "description": "Array of URLs to be applied to containers of a service.", diff --git a/openapi.yaml b/openapi.yaml index 78d482b23c..2871e12613 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -12154,6 +12154,10 @@ paths: docker_compose_raw: type: string description: 'The base64 encoded Docker Compose content.' + connect_to_docker_network: + type: boolean + default: false + description: 'Connect the service to the predefined docker network.' urls: type: array description: 'Array of URLs to be applied to containers of a service.' diff --git a/tests/Feature/ServiceConnectToDockerNetworkApiTest.php b/tests/Feature/ServiceConnectToDockerNetworkApiTest.php new file mode 100644 index 0000000000..1753a20d34 --- /dev/null +++ b/tests/Feature/ServiceConnectToDockerNetworkApiTest.php @@ -0,0 +1,78 @@ + InstanceSettings::create(['id' => 0, 'is_api_enabled' => true])); + + $this->team = Team::factory()->create(); + $this->user = User::factory()->create(); + $this->team->members()->attach($this->user->id, ['role' => 'owner']); + + session(['currentTeam' => $this->team]); + + $this->bearerToken = $this->user->createToken('test-token', ['*'])->plainTextToken; + + $this->server = Server::factory()->create(['team_id' => $this->team->id]); + $this->project = Project::factory()->create(['team_id' => $this->team->id]); + $this->environment = $this->project->environments()->first(); +}); + +function createServiceWithDockerNetworkFlag(mixed $connectToDockerNetwork, string $bearerToken, array $payload): TestResponse +{ + return test()->withHeaders(['Authorization' => 'Bearer '.$bearerToken]) + ->postJson('/api/v1/services', [ + ...$payload, + 'connect_to_docker_network' => $connectToDockerNetwork, + ]); +} + +describe('POST /api/v1/services connect_to_docker_network', function () { + test('saves the flag for a docker_compose_raw service', function () { + $response = createServiceWithDockerNetworkFlag(true, $this->bearerToken, [ + 'project_uuid' => $this->project->uuid, + 'environment_uuid' => $this->environment->uuid, + 'server_uuid' => $this->server->uuid, + 'docker_compose_raw' => base64_encode("services:\n app:\n image: nginx:alpine\n"), + ]); + + $response->assertCreated(); + expect(Service::whereUuid($response->json('uuid'))->firstOrFail()->connect_to_docker_network)->toBeTruthy(); + }); + + test('saves the flag for a one-click service', function () { + Queue::fake(); + + $response = createServiceWithDockerNetworkFlag(true, $this->bearerToken, [ + 'type' => 'cyberchef', + 'project_uuid' => $this->project->uuid, + 'environment_uuid' => $this->environment->uuid, + 'server_uuid' => $this->server->uuid, + ]); + + $response->assertCreated(); + expect(Service::whereUuid($response->json('uuid'))->firstOrFail()->connect_to_docker_network)->toBeTruthy(); + }); + + test('rejects a non-boolean value', function () { + $response = createServiceWithDockerNetworkFlag('not-a-boolean', $this->bearerToken, [ + 'project_uuid' => $this->project->uuid, + 'environment_uuid' => $this->environment->uuid, + 'server_uuid' => $this->server->uuid, + 'docker_compose_raw' => base64_encode("services:\n app:\n image: nginx:alpine\n"), + ]); + + $response->assertUnprocessable()->assertJsonValidationErrors(['connect_to_docker_network']); + expect($response->json('errors.connect_to_docker_network'))->not->toContain('This field is not allowed.'); + }); +});