fix(api): keep omitted flags when patching application env variables

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-10-24 18:14:24 +02:00
co-authored by Claude Opus 5.5
parent 9395c415b3
commit d9a22ce10e
2 changed files with 93 additions and 123 deletions
@@ -4047,95 +4047,28 @@ class ApplicationsController extends Controller
], 422);
}
$is_preview = $request->is_preview ?? false;
$is_literal = $request->is_literal ?? false;
$key = str($request->key)->trim()->replace(' ', '_')->value;
if ($is_preview) {
$env = $application->environment_variables_preview->where('key', $key)->first();
if ($env) {
$env->value = $request->value;
if ($env->is_literal != $is_literal) {
$env->is_literal = $is_literal;
}
if ($env->is_preview != $is_preview) {
$env->is_preview = $is_preview;
}
if ($env->is_multiline != $request->is_multiline) {
$env->is_multiline = $request->is_multiline;
}
if ($env->is_shown_once != $request->is_shown_once) {
$env->is_shown_once = $request->is_shown_once;
}
if ($request->has('is_runtime') && $env->is_runtime != $request->is_runtime) {
$env->is_runtime = $request->is_runtime;
}
if ($request->has('is_buildtime') && $env->is_buildtime != $request->is_buildtime) {
$env->is_buildtime = $request->is_buildtime;
}
if ($request->has('comment') && $env->comment != $request->comment) {
$env->comment = $request->comment;
}
$env->save();
auditLog('api.application.env_updated', [
'team_id' => $teamId,
'application_uuid' => $application->uuid,
'env_uuid' => $env->uuid,
'env_key' => $env->key,
'is_preview' => (bool) $is_preview,
]);
return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
} else {
return response()->json([
'message' => 'Environment variable not found.',
], 404);
}
} else {
$env = $application->environment_variables->where('key', $key)->first();
if ($env) {
$env->value = $request->value;
if ($env->is_literal != $is_literal) {
$env->is_literal = $is_literal;
}
if ($env->is_preview != $is_preview) {
$env->is_preview = $is_preview;
}
if ($env->is_multiline != $request->is_multiline) {
$env->is_multiline = $request->is_multiline;
}
if ($env->is_shown_once != $request->is_shown_once) {
$env->is_shown_once = $request->is_shown_once;
}
if ($request->has('is_runtime') && $env->is_runtime != $request->is_runtime) {
$env->is_runtime = $request->is_runtime;
}
if ($request->has('is_buildtime') && $env->is_buildtime != $request->is_buildtime) {
$env->is_buildtime = $request->is_buildtime;
}
if ($request->has('comment') && $env->comment != $request->comment) {
$env->comment = $request->comment;
}
$env->save();
auditLog('api.application.env_updated', [
'team_id' => $teamId,
'application_uuid' => $application->uuid,
'env_uuid' => $env->uuid,
'env_key' => $env->key,
'is_preview' => (bool) $is_preview,
]);
return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
} else {
return response()->json([
'message' => 'Environment variable not found.',
], 404);
}
$environmentVariables = $is_preview ? $application->environment_variables_preview : $application->environment_variables;
$env = $environmentVariables->where('key', $key)->first();
if (! $env) {
return response()->json([
'message' => 'Environment variable not found.',
], 404);
}
return response()->json([
'message' => 'Something is not okay. Are you okay?',
], 500);
$env->value = $request->value;
$env->fill($request->only(['is_literal', 'is_multiline', 'is_shown_once', 'is_runtime', 'is_buildtime', 'comment']));
$env->save();
auditLog('api.application.env_updated', [
'team_id' => $teamId,
'application_uuid' => $application->uuid,
'env_uuid' => $env->uuid,
'env_key' => $env->key,
'is_preview' => (bool) $is_preview,
]);
return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
}
#[OA\Patch(
@@ -4280,25 +4213,7 @@ class ApplicationsController extends Controller
$env = $application->environment_variables_preview()->where('key', $key)->first();
if ($env) {
$env->value = $item->get('value');
if ($env->is_literal != $is_literal) {
$env->is_literal = $is_literal;
}
if ($env->is_multiline != $item->get('is_multiline')) {
$env->is_multiline = $item->get('is_multiline');
}
if ($env->is_shown_once != $item->get('is_shown_once')) {
$env->is_shown_once = $item->get('is_shown_once');
}
if ($item->has('is_runtime') && $env->is_runtime != $item->get('is_runtime')) {
$env->is_runtime = $item->get('is_runtime');
}
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->fill($item->only(['is_literal', 'is_multiline', 'is_shown_once', 'is_runtime', 'is_buildtime', 'comment'])->all());
$env->save();
} else {
$env = $application->environment_variables()->create([
@@ -4319,24 +4234,7 @@ class ApplicationsController extends Controller
$env = $application->environment_variables()->where('key', $key)->first();
if ($env) {
$env->value = $item->get('value');
if ($env->is_literal != $is_literal) {
$env->is_literal = $is_literal;
}
if ($env->is_multiline != $item->get('is_multiline')) {
$env->is_multiline = $item->get('is_multiline');
}
if ($env->is_shown_once != $item->get('is_shown_once')) {
$env->is_shown_once = $item->get('is_shown_once');
}
if ($item->has('is_runtime') && $env->is_runtime != $item->get('is_runtime')) {
$env->is_runtime = $item->get('is_runtime');
}
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->fill($item->only(['is_literal', 'is_multiline', 'is_shown_once', 'is_runtime', 'is_buildtime', 'comment'])->all());
$env->save();
} else {
$env = $application->environment_variables()->create([
@@ -253,6 +253,78 @@ describe('PATCH /api/v1/applications/{uuid}/envs', function () {
});
});
describe('application env PATCH keeps omitted flags', function () {
beforeEach(function () {
$this->application = Application::factory()->create([
'environment_id' => $this->environment->id,
'destination_id' => $this->destination->id,
'destination_type' => $this->destination->getMorphClass(),
]);
$this->env = EnvironmentVariable::create([
'key' => 'FLAGGED_KEY',
'value' => 'old-value',
'resourceable_type' => Application::class,
'resourceable_id' => $this->application->id,
'is_preview' => false,
'is_literal' => true,
'is_multiline' => true,
'is_shown_once' => true,
]);
$this->headers = [
'Authorization' => 'Bearer '.$this->bearerToken,
'Content-Type' => 'application/json',
];
});
test('single update with only key and value keeps flags', function () {
$this->withHeaders($this->headers)
->patchJson("/api/v1/applications/{$this->application->uuid}/envs", [
'key' => 'FLAGGED_KEY',
'value' => 'new-value',
])
->assertStatus(201);
$env = $this->env->fresh();
expect($env->value)->toBe('new-value')
->and((bool) $env->is_literal)->toBeTrue()
->and($env->is_multiline)->toBeTrue()
->and((bool) $env->is_shown_once)->toBeTrue();
});
test('bulk update with only key and value keeps flags', function () {
$this->withHeaders($this->headers)
->patchJson("/api/v1/applications/{$this->application->uuid}/envs/bulk", [
'data' => [['key' => 'FLAGGED_KEY', 'value' => 'new-value']],
])
->assertStatus(201);
$env = $this->env->fresh();
expect($env->value)->toBe('new-value')
->and((bool) $env->is_literal)->toBeTrue()
->and($env->is_multiline)->toBeTrue()
->and((bool) $env->is_shown_once)->toBeTrue();
});
test('explicit flags are still applied', function () {
$this->withHeaders($this->headers)
->patchJson("/api/v1/applications/{$this->application->uuid}/envs", [
'key' => 'FLAGGED_KEY',
'value' => 'new-value',
'is_literal' => false,
'is_multiline' => false,
'is_shown_once' => false,
])
->assertStatus(201);
$env = $this->env->fresh();
expect((bool) $env->is_literal)->toBeFalse()
->and($env->is_multiline)->toBeFalse()
->and((bool) $env->is_shown_once)->toBeFalse();
});
});
describe('environment variable key validation for app and service APIs', function () {
test('rejects invalid service environment variable keys on create update and bulk', function () {
$service = Service::factory()->create([