From 38299ab507fef0110639ed278141c4876c4a1e5a Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 23 Jul 2024 11:36:05 +0200 Subject: [PATCH] feat: create/delete project endpoints --- .../Controllers/Api/ProjectController.php | 181 +++++++++++++++++- app/Models/Project.php | 2 +- openapi.yaml | 76 ++++++++ routes/api.php | 3 + 4 files changed, 259 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index b7c3d115d..aa3e5552b 100644 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -135,8 +135,14 @@ public function environment_details(Request $request) if (is_null($teamId)) { return invalidTokenResponse(); } - $project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); - $environment = $project->environments()->whereName(request()->environment_name)->first(); + if (! $request->uuid) { + return response()->json(['message' => 'Uuid is required.'], 422); + } + if (! $request->environment_name) { + return response()->json(['message' => 'Environment name is required.'], 422); + } + $project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first(); + $environment = $project->environments()->whereName($request->environment_name)->first(); if (! $environment) { return response()->json(['message' => 'Environment not found.'], 404); } @@ -144,4 +150,175 @@ public function environment_details(Request $request) return response()->json(serializeApiResponse($environment)); } + + #[OA\Post( + summary: 'Create Project', + description: 'Create Project.', + path: '/projects', + security: [ + ['bearerAuth' => []], + ], + tags: ['Projects'], + requestBody: new OA\RequestBody( + required: true, + description: 'Project created.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'name' => ['type' => 'string', 'description' => 'The name of the project.'], + 'description' => ['type' => 'string', 'description' => 'The description of the project.'], + ], + ), + ), + ), + responses: [ + new OA\Response( + response: 201, + description: 'Project created.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string', 'example' => 'og888os'], + 'name' => ['type' => 'string', 'example' => 'Project Name'], + 'description' => ['type' => 'string', 'example' => 'Project Description'], + ] + ) + ), + ]), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 404, + ref: '#/components/responses/404', + ), + ] + )] + public function create_project(Request $request) + { + $allowedFields = ['name', 'description']; + + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + $validator = customApiValidator($request->all(), [ + 'name' => 'string|max:255', + 'description' => 'string|nullable', + ]); + + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } + + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + + $project = Project::create([ + 'name' => $request->name, + 'description' => $request->description, + 'team_id' => $teamId, + ]); + + return response()->json([ + 'uuid' => $project->uuid, + 'name' => $project->name, + 'description' => $project->description, + ])->status(201); + } + + #[OA\Delete( + summary: 'Delete', + description: 'Delete project by UUID.', + path: '/projects/{uuid}', + security: [ + ['bearerAuth' => []], + ], + tags: ['Projects'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the application.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Project deleted.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Project deleted.'], + ] + ) + ), + ]), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 404, + ref: '#/components/responses/404', + ), + ] + )] + public function delete_project(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + if (! $request->uuid) { + return response()->json(['message' => 'Uuid is required.'], 422); + } + $project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first(); + if (! $project) { + return response()->json(['message' => 'Project not found.'], 404); + } + if ($project->resource_count() > 0) { + return response()->json(['message' => 'Project has resources, so it cannot be deleted.'], 400); + } + + $project->delete(); + + return response()->json(['message' => 'Project deleted.']); + } } diff --git a/app/Models/Project.php b/app/Models/Project.php index d4310e349..77f62d770 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -122,7 +122,7 @@ public function mariadbs() public function resource_count() { - return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count() + $this->services()->count() + $this->clickhouses()->count(); + return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count() + $this->clickhouses()->count() + $this->services()->count(); } public function databases() diff --git a/openapi.yaml b/openapi.yaml index 8b3c170d0..98bb37914 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3010,6 +3010,46 @@ paths: security: - bearerAuth: [] + post: + tags: + - Projects + summary: 'Create Project' + description: 'Create Project.' + operationId: cf067eb7cf18216cda3239329a2eeadb + requestBody: + description: 'Project created.' + required: true + content: + application/json: + schema: + properties: + name: + type: string + description: 'The name of the project.' + description: + type: string + description: 'The description of the project.' + type: object + responses: + '201': + description: 'Project created.' + content: + application/json: + schema: + properties: + uuid: { type: string, example: og888os } + name: { type: string, example: 'Project Name' } + description: { type: string, example: 'Project Description' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] '/projects/{uuid}': get: tags: @@ -3041,6 +3081,39 @@ paths: security: - bearerAuth: [] + delete: + tags: + - Projects + summary: Delete + description: 'Delete project by UUID.' + operationId: f668a936f505b4401948c74b6a663029 + parameters: + - + name: uuid + in: path + description: 'UUID of the application.' + required: true + schema: + type: string + format: uuid + responses: + '200': + description: 'Project deleted.' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Project deleted.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] '/projects/{uuid}/{environment_name}': get: tags: @@ -4480,6 +4553,9 @@ components: is_container_label_escape_enabled: type: boolean description: 'The flag to enable the container label escape.' + is_container_label_readonly_enabled: + type: boolean + description: 'The flag to enable the container label readonly.' config_hash: type: string description: 'The hash of the service configuration.' diff --git a/routes/api.php b/routes/api.php index 854928730..2224722e4 100644 --- a/routes/api.php +++ b/routes/api.php @@ -41,6 +41,9 @@ Route::get('/projects/{uuid}', [ProjectController::class, 'project_by_uuid']); Route::get('/projects/{uuid}/{environment_name}', [ProjectController::class, 'environment_details']); + Route::post('/projects', [ProjectController::class, 'create_project']); + Route::delete('/projects/{uuid}', [ProjectController::class, 'delete_project']); + Route::get('/security/keys', [SecurityController::class, 'keys']); Route::post('/security/keys', [SecurityController::class, 'create_key'])->middleware([IgnoreReadOnlyApiToken::class]);