From 2a52fb5872d9321dc89259f83de4af5da8425e8e Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Wed, 26 Jun 2024 13:32:36 +0200 Subject: [PATCH] feat: bulk env update api endpoint --- app/Http/Controllers/Api/Applications.php | 144 +++++++++++++++++++++- bootstrap/helpers/shared.php | 11 ++ routes/api.php | 1 + 3 files changed, 152 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/Applications.php index d19e73a1b..8bbb14a07 100644 --- a/app/Http/Controllers/Api/Applications.php +++ b/app/Http/Controllers/Api/Applications.php @@ -10,7 +10,6 @@ use App\Models\EnvironmentVariable; use App\Models\Project; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; use Visus\Cuid2\Cuid2; @@ -101,7 +100,8 @@ public function update_by_uuid(Request $request) } $server = $application->destination->server; $allowedFields = ['name', 'description', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'static_image', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'watch_paths', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'docker_compose_location', 'docker_compose', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'redirect']; - $validator = Validator::make($request->all(), [ + + $validator = customApiValidator($request->all(), [ 'name' => 'string|max:255', 'description' => 'string|nullable', 'domains' => 'string', @@ -290,7 +290,7 @@ public function update_env_by_uuid(Request $request) 'message' => 'Application not found', ], 404); } - $validator = Validator::make($request->all(), [ + $validator = customApiValidator($request->all(), [ 'key' => 'string|required', 'value' => 'string|nullable', 'is_preview' => 'boolean', @@ -399,6 +399,142 @@ public function update_env_by_uuid(Request $request) } + public function create_bulk_envs(Request $request) + { + ray()->clearAll(); + $teamId = get_team_id_from_token(); + + if (is_null($teamId)) { + return invalid_token(); + } + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + + if (! $application) { + return response()->json([ + 'success' => false, + 'message' => 'Application not found', + ], 404); + } + + $bulk_data = $request->get('data'); + if (! $bulk_data) { + return response()->json([ + 'message' => 'Bulk data is required.', + ], 400); + } + $bulk_data = collect($bulk_data)->map(function ($item) { + return collect($item)->only(['key', 'value', 'is_preview', 'is_build_time', 'is_literal', 'both']); + }); + foreach ($bulk_data as $item) { + $validator = customApiValidator($item, [ + 'key' => 'string|required', + 'value' => 'string|nullable', + 'is_preview' => 'boolean', + 'is_build_time' => 'boolean', + 'is_literal' => 'boolean', + 'both' => 'boolean', + ]); + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + $is_preview = $item->get('is_preview') ?? false; + $is_build_time = $item->get('is_build_time') ?? false; + $is_literal = $item->get('is_literal') ?? false; + $both = $item->get('both') ?? false; + if ($both) { + $env = $application->environment_variables_preview->where('key', $item->get('key'))->first(); + if ($env) { + $env->value = $item->get('value'); + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + $env->save(); + } else { + $env = $application->environment_variables()->create([ + 'key' => $item->get('key'), + 'value' => $item->get('value'), + 'is_preview' => $is_preview, + 'is_build_time' => $is_build_time, + 'is_literal' => $is_literal, + ]); + } + + $env = $application->environment_variables->where('key', $item->get('key'))->first(); + if ($env) { + $env->value = $item->get('value'); + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + $env->save(); + } else { + $env = $application->environment_variables()->create([ + 'key' => $item->get('key'), + 'value' => $item->get('value'), + 'is_preview' => $is_preview, + 'is_build_time' => $is_build_time, + 'is_literal' => $is_literal, + ]); + } + + continue; + } + if ($is_preview) { + $env = $application->environment_variables_preview->where('key', $item->get('key'))->first(); + if ($env) { + $env->value = $item->get('value'); + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + $env->save(); + } else { + $env = $application->environment_variables()->create([ + 'key' => $item->get('key'), + 'value' => $item->get('value'), + 'is_preview' => $is_preview, + 'is_build_time' => $is_build_time, + 'is_literal' => $is_literal, + ]); + } + } else { + $env = $application->environment_variables->where('key', $item->get('key'))->first(); + if ($env) { + $env->value = $item->get('value'); + if ($env->is_build_time != $is_build_time) { + $env->is_build_time = $is_build_time; + } + if ($env->is_literal != $is_literal) { + $env->is_literal = $is_literal; + } + $env->save(); + } else { + $env = $application->environment_variables()->create([ + 'key' => $item->get('key'), + 'value' => $item->get('value'), + 'is_preview' => $is_preview, + 'is_build_time' => $is_build_time, + 'is_literal' => $is_literal, + ]); + } + } + } + + return response()->json([ + 'message' => 'Environments updated.', + ]); + } + public function create_env(Request $request) { ray()->clearAll(); @@ -416,7 +552,7 @@ public function create_env(Request $request) 'message' => 'Application not found', ], 404); } - $validator = Validator::make($request->all(), [ + $validator = customApiValidator($request->all(), [ 'key' => 'string|required', 'value' => 'string|nullable', 'is_preview' => 'boolean', diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 9d9c92330..a4676cfd4 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -40,6 +40,7 @@ use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; use Illuminate\Support\Stringable; use Lcobucci\JWT\Encoding\ChainedFormatter; @@ -2319,3 +2320,13 @@ function isBase64Encoded($strValue) { return base64_encode(base64_decode($strValue, true)) === $strValue; } +function customApiValidator(Collection|array $item, array $rules) +{ + if (is_array($item)) { + $item = collect($item); + } + + return Validator::make($item->toArray(), $rules, [ + 'required' => 'This field is required.', + ]); +} diff --git a/routes/api.php b/routes/api.php index 41a369f3d..7aca146ba 100644 --- a/routes/api.php +++ b/routes/api.php @@ -50,6 +50,7 @@ Route::get('/applications/{uuid}/envs', [Applications::class, 'envs_by_uuid']); Route::post('/applications/{uuid}/envs', [Applications::class, 'create_env']); + Route::post('/applications/{uuid}/envs/bulk', [Applications::class, 'create_bulk_envs']); Route::patch('/applications/{uuid}/envs', [Applications::class, 'update_env_by_uuid']); Route::delete('/applications/{uuid}/envs/{env_uuid}', [Applications::class, 'delete_env_by_uuid']);