feat(api): support comments in bulk environment variable endpoints
Add support for optional comment field on environment variables created or updated through the bulk API endpoints. Comments are validated to a maximum of 256 characters and are nullable. Updates preserve existing comments when not provided in the request.
This commit is contained in:
parent
8a164735cb
commit
fb76b68c08
3 changed files with 255 additions and 1 deletions
|
|
@ -3175,7 +3175,7 @@ public function create_bulk_envs(Request $request)
|
|||
], 400);
|
||||
}
|
||||
$bulk_data = collect($bulk_data)->map(function ($item) {
|
||||
return collect($item)->only(['key', 'value', 'is_preview', 'is_literal', 'is_multiline', 'is_shown_once', 'is_runtime', 'is_buildtime']);
|
||||
return collect($item)->only(['key', 'value', 'is_preview', 'is_literal', 'is_multiline', 'is_shown_once', 'is_runtime', 'is_buildtime', 'comment']);
|
||||
});
|
||||
$returnedEnvs = collect();
|
||||
foreach ($bulk_data as $item) {
|
||||
|
|
@ -3188,6 +3188,7 @@ public function create_bulk_envs(Request $request)
|
|||
'is_shown_once' => 'boolean',
|
||||
'is_runtime' => 'boolean',
|
||||
'is_buildtime' => 'boolean',
|
||||
'comment' => 'string|nullable|max:256',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
|
|
@ -3220,6 +3221,9 @@ public function create_bulk_envs(Request $request)
|
|||
if ($item->has('is_buildtime') && $env->is_buildtime != $item->get('is_buildtime')) {
|
||||
$env->is_buildtime = $item->get('is_buildtime');
|
||||
}
|
||||
if ($item->has('comment') && $env->comment != $item->get('comment')) {
|
||||
$env->comment = $item->get('comment');
|
||||
}
|
||||
$env->save();
|
||||
} else {
|
||||
$env = $application->environment_variables()->create([
|
||||
|
|
@ -3231,6 +3235,7 @@ public function create_bulk_envs(Request $request)
|
|||
'is_shown_once' => $is_shown_once,
|
||||
'is_runtime' => $item->get('is_runtime', true),
|
||||
'is_buildtime' => $item->get('is_buildtime', true),
|
||||
'comment' => $item->get('comment'),
|
||||
'resourceable_type' => get_class($application),
|
||||
'resourceable_id' => $application->id,
|
||||
]);
|
||||
|
|
@ -3254,6 +3259,9 @@ public function create_bulk_envs(Request $request)
|
|||
if ($item->has('is_buildtime') && $env->is_buildtime != $item->get('is_buildtime')) {
|
||||
$env->is_buildtime = $item->get('is_buildtime');
|
||||
}
|
||||
if ($item->has('comment') && $env->comment != $item->get('comment')) {
|
||||
$env->comment = $item->get('comment');
|
||||
}
|
||||
$env->save();
|
||||
} else {
|
||||
$env = $application->environment_variables()->create([
|
||||
|
|
@ -3265,6 +3273,7 @@ public function create_bulk_envs(Request $request)
|
|||
'is_shown_once' => $is_shown_once,
|
||||
'is_runtime' => $item->get('is_runtime', true),
|
||||
'is_buildtime' => $item->get('is_buildtime', true),
|
||||
'comment' => $item->get('comment'),
|
||||
'resourceable_type' => get_class($application),
|
||||
'resourceable_id' => $application->id,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1362,6 +1362,7 @@ public function create_bulk_envs(Request $request)
|
|||
'is_literal' => 'boolean',
|
||||
'is_multiline' => 'boolean',
|
||||
'is_shown_once' => 'boolean',
|
||||
'comment' => 'string|nullable|max:256',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
|
|
|
|||
244
tests/Feature/EnvironmentVariableBulkCommentApiTest.php
Normal file
244
tests/Feature/EnvironmentVariableBulkCommentApiTest.php
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\Environment;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Project;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
InstanceSettings::updateOrCreate(['id' => 0]);
|
||||
|
||||
$this->team = Team::factory()->create();
|
||||
$this->user = User::factory()->create();
|
||||
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
|
||||
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
$this->token = $this->user->createToken('test-token', ['*']);
|
||||
$this->bearerToken = $this->token->plainTextToken;
|
||||
|
||||
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->destination = StandaloneDocker::where('server_id', $this->server->id)->first();
|
||||
$this->project = Project::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->environment = Environment::factory()->create(['project_id' => $this->project->id]);
|
||||
});
|
||||
|
||||
describe('PATCH /api/v1/applications/{uuid}/envs/bulk', function () {
|
||||
test('creates environment variables with comments', function () {
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'destination_id' => $this->destination->id,
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||
'Content-Type' => 'application/json',
|
||||
])->patchJson("/api/v1/applications/{$application->uuid}/envs/bulk", [
|
||||
'data' => [
|
||||
[
|
||||
'key' => 'DB_HOST',
|
||||
'value' => 'localhost',
|
||||
'comment' => 'Database host for production',
|
||||
],
|
||||
[
|
||||
'key' => 'DB_PORT',
|
||||
'value' => '5432',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
|
||||
$envWithComment = EnvironmentVariable::where('key', 'DB_HOST')
|
||||
->where('resourceable_id', $application->id)
|
||||
->where('is_preview', false)
|
||||
->first();
|
||||
|
||||
$envWithoutComment = EnvironmentVariable::where('key', 'DB_PORT')
|
||||
->where('resourceable_id', $application->id)
|
||||
->where('is_preview', false)
|
||||
->first();
|
||||
|
||||
expect($envWithComment->comment)->toBe('Database host for production');
|
||||
expect($envWithoutComment->comment)->toBeNull();
|
||||
});
|
||||
|
||||
test('updates existing environment variable comment', function () {
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'destination_id' => $this->destination->id,
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
EnvironmentVariable::create([
|
||||
'key' => 'API_KEY',
|
||||
'value' => 'old-key',
|
||||
'comment' => 'Old comment',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $application->id,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||
'Content-Type' => 'application/json',
|
||||
])->patchJson("/api/v1/applications/{$application->uuid}/envs/bulk", [
|
||||
'data' => [
|
||||
[
|
||||
'key' => 'API_KEY',
|
||||
'value' => 'new-key',
|
||||
'comment' => 'Updated comment',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
|
||||
$env = EnvironmentVariable::where('key', 'API_KEY')
|
||||
->where('resourceable_id', $application->id)
|
||||
->where('is_preview', false)
|
||||
->first();
|
||||
|
||||
expect($env->value)->toBe('new-key');
|
||||
expect($env->comment)->toBe('Updated comment');
|
||||
});
|
||||
|
||||
test('preserves existing comment when not provided in bulk update', function () {
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'destination_id' => $this->destination->id,
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
EnvironmentVariable::create([
|
||||
'key' => 'SECRET',
|
||||
'value' => 'old-secret',
|
||||
'comment' => 'Keep this comment',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $application->id,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||
'Content-Type' => 'application/json',
|
||||
])->patchJson("/api/v1/applications/{$application->uuid}/envs/bulk", [
|
||||
'data' => [
|
||||
[
|
||||
'key' => 'SECRET',
|
||||
'value' => 'new-secret',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
|
||||
$env = EnvironmentVariable::where('key', 'SECRET')
|
||||
->where('resourceable_id', $application->id)
|
||||
->where('is_preview', false)
|
||||
->first();
|
||||
|
||||
expect($env->value)->toBe('new-secret');
|
||||
expect($env->comment)->toBe('Keep this comment');
|
||||
});
|
||||
|
||||
test('rejects comment exceeding 256 characters', function () {
|
||||
$application = Application::factory()->create([
|
||||
'environment_id' => $this->environment->id,
|
||||
'destination_id' => $this->destination->id,
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||
'Content-Type' => 'application/json',
|
||||
])->patchJson("/api/v1/applications/{$application->uuid}/envs/bulk", [
|
||||
'data' => [
|
||||
[
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'value',
|
||||
'comment' => str_repeat('a', 257),
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PATCH /api/v1/services/{uuid}/envs/bulk', function () {
|
||||
test('creates environment variables with comments', function () {
|
||||
$service = Service::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'destination_id' => $this->destination->id,
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
'environment_id' => $this->environment->id,
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||
'Content-Type' => 'application/json',
|
||||
])->patchJson("/api/v1/services/{$service->uuid}/envs/bulk", [
|
||||
'data' => [
|
||||
[
|
||||
'key' => 'REDIS_HOST',
|
||||
'value' => 'redis',
|
||||
'comment' => 'Redis cache host',
|
||||
],
|
||||
[
|
||||
'key' => 'REDIS_PORT',
|
||||
'value' => '6379',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
|
||||
$envWithComment = EnvironmentVariable::where('key', 'REDIS_HOST')
|
||||
->where('resourceable_id', $service->id)
|
||||
->where('resourceable_type', Service::class)
|
||||
->first();
|
||||
|
||||
$envWithoutComment = EnvironmentVariable::where('key', 'REDIS_PORT')
|
||||
->where('resourceable_id', $service->id)
|
||||
->where('resourceable_type', Service::class)
|
||||
->first();
|
||||
|
||||
expect($envWithComment->comment)->toBe('Redis cache host');
|
||||
expect($envWithoutComment->comment)->toBeNull();
|
||||
});
|
||||
|
||||
test('rejects comment exceeding 256 characters', function () {
|
||||
$service = Service::factory()->create([
|
||||
'server_id' => $this->server->id,
|
||||
'destination_id' => $this->destination->id,
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
'environment_id' => $this->environment->id,
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$this->bearerToken,
|
||||
'Content-Type' => 'application/json',
|
||||
])->patchJson("/api/v1/services/{$service->uuid}/envs/bulk", [
|
||||
'data' => [
|
||||
[
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'value',
|
||||
'comment' => str_repeat('a', 257),
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue