Skip to content

Commit 38299ab

Browse files
committed
feat: create/delete project endpoints
1 parent f134171 commit 38299ab

File tree

4 files changed

+259
-3
lines changed

4 files changed

+259
-3
lines changed

app/Http/Controllers/Api/ProjectController.php

Lines changed: 179 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,190 @@ public function environment_details(Request $request)
135135
if (is_null($teamId)) {
136136
return invalidTokenResponse();
137137
}
138-
$project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
139-
$environment = $project->environments()->whereName(request()->environment_name)->first();
138+
if (! $request->uuid) {
139+
return response()->json(['message' => 'Uuid is required.'], 422);
140+
}
141+
if (! $request->environment_name) {
142+
return response()->json(['message' => 'Environment name is required.'], 422);
143+
}
144+
$project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first();
145+
$environment = $project->environments()->whereName($request->environment_name)->first();
140146
if (! $environment) {
141147
return response()->json(['message' => 'Environment not found.'], 404);
142148
}
143149
$environment = $environment->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']);
144150

145151
return response()->json(serializeApiResponse($environment));
146152
}
153+
154+
#[OA\Post(
155+
summary: 'Create Project',
156+
description: 'Create Project.',
157+
path: '/projects',
158+
security: [
159+
['bearerAuth' => []],
160+
],
161+
tags: ['Projects'],
162+
requestBody: new OA\RequestBody(
163+
required: true,
164+
description: 'Project created.',
165+
content: new OA\MediaType(
166+
mediaType: 'application/json',
167+
schema: new OA\Schema(
168+
type: 'object',
169+
properties: [
170+
'name' => ['type' => 'string', 'description' => 'The name of the project.'],
171+
'description' => ['type' => 'string', 'description' => 'The description of the project.'],
172+
],
173+
),
174+
),
175+
),
176+
responses: [
177+
new OA\Response(
178+
response: 201,
179+
description: 'Project created.',
180+
content: [
181+
new OA\MediaType(
182+
mediaType: 'application/json',
183+
schema: new OA\Schema(
184+
type: 'object',
185+
properties: [
186+
'uuid' => ['type' => 'string', 'example' => 'og888os'],
187+
'name' => ['type' => 'string', 'example' => 'Project Name'],
188+
'description' => ['type' => 'string', 'example' => 'Project Description'],
189+
]
190+
)
191+
),
192+
]),
193+
new OA\Response(
194+
response: 401,
195+
ref: '#/components/responses/401',
196+
),
197+
new OA\Response(
198+
response: 400,
199+
ref: '#/components/responses/400',
200+
),
201+
new OA\Response(
202+
response: 404,
203+
ref: '#/components/responses/404',
204+
),
205+
]
206+
)]
207+
public function create_project(Request $request)
208+
{
209+
$allowedFields = ['name', 'description'];
210+
211+
$teamId = getTeamIdFromToken();
212+
if (is_null($teamId)) {
213+
return invalidTokenResponse();
214+
}
215+
216+
$return = validateIncomingRequest($request);
217+
if ($return instanceof \Illuminate\Http\JsonResponse) {
218+
return $return;
219+
}
220+
$validator = customApiValidator($request->all(), [
221+
'name' => 'string|max:255',
222+
'description' => 'string|nullable',
223+
]);
224+
225+
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
226+
if ($validator->fails() || ! empty($extraFields)) {
227+
$errors = $validator->errors();
228+
if (! empty($extraFields)) {
229+
foreach ($extraFields as $field) {
230+
$errors->add($field, 'This field is not allowed.');
231+
}
232+
}
233+
234+
return response()->json([
235+
'message' => 'Validation failed.',
236+
'errors' => $errors,
237+
], 422);
238+
}
239+
240+
$project = Project::create([
241+
'name' => $request->name,
242+
'description' => $request->description,
243+
'team_id' => $teamId,
244+
]);
245+
246+
return response()->json([
247+
'uuid' => $project->uuid,
248+
'name' => $project->name,
249+
'description' => $project->description,
250+
])->status(201);
251+
}
252+
253+
#[OA\Delete(
254+
summary: 'Delete',
255+
description: 'Delete project by UUID.',
256+
path: '/projects/{uuid}',
257+
security: [
258+
['bearerAuth' => []],
259+
],
260+
tags: ['Projects'],
261+
parameters: [
262+
new OA\Parameter(
263+
name: 'uuid',
264+
in: 'path',
265+
description: 'UUID of the application.',
266+
required: true,
267+
schema: new OA\Schema(
268+
type: 'string',
269+
format: 'uuid',
270+
)
271+
),
272+
],
273+
responses: [
274+
new OA\Response(
275+
response: 200,
276+
description: 'Project deleted.',
277+
content: [
278+
new OA\MediaType(
279+
mediaType: 'application/json',
280+
schema: new OA\Schema(
281+
type: 'object',
282+
properties: [
283+
'message' => ['type' => 'string', 'example' => 'Project deleted.'],
284+
]
285+
)
286+
),
287+
]),
288+
new OA\Response(
289+
response: 401,
290+
ref: '#/components/responses/401',
291+
),
292+
new OA\Response(
293+
response: 400,
294+
ref: '#/components/responses/400',
295+
),
296+
new OA\Response(
297+
response: 404,
298+
ref: '#/components/responses/404',
299+
),
300+
]
301+
)]
302+
public function delete_project(Request $request)
303+
{
304+
$teamId = getTeamIdFromToken();
305+
if (is_null($teamId)) {
306+
return invalidTokenResponse();
307+
}
308+
309+
if (! $request->uuid) {
310+
return response()->json(['message' => 'Uuid is required.'], 422);
311+
}
312+
$project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first();
313+
if (! $project) {
314+
return response()->json(['message' => 'Project not found.'], 404);
315+
}
316+
if ($project->resource_count() > 0) {
317+
return response()->json(['message' => 'Project has resources, so it cannot be deleted.'], 400);
318+
}
319+
320+
$project->delete();
321+
322+
return response()->json(['message' => 'Project deleted.']);
323+
}
147324
}

app/Models/Project.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public function mariadbs()
122122

123123
public function resource_count()
124124
{
125-
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();
125+
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();
126126
}
127127

128128
public function databases()

openapi.yaml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3010,6 +3010,46 @@ paths:
30103010
security:
30113011
-
30123012
bearerAuth: []
3013+
post:
3014+
tags:
3015+
- Projects
3016+
summary: 'Create Project'
3017+
description: 'Create Project.'
3018+
operationId: cf067eb7cf18216cda3239329a2eeadb
3019+
requestBody:
3020+
description: 'Project created.'
3021+
required: true
3022+
content:
3023+
application/json:
3024+
schema:
3025+
properties:
3026+
name:
3027+
type: string
3028+
description: 'The name of the project.'
3029+
description:
3030+
type: string
3031+
description: 'The description of the project.'
3032+
type: object
3033+
responses:
3034+
'201':
3035+
description: 'Project created.'
3036+
content:
3037+
application/json:
3038+
schema:
3039+
properties:
3040+
uuid: { type: string, example: og888os }
3041+
name: { type: string, example: 'Project Name' }
3042+
description: { type: string, example: 'Project Description' }
3043+
type: object
3044+
'401':
3045+
$ref: '#/components/responses/401'
3046+
'400':
3047+
$ref: '#/components/responses/400'
3048+
'404':
3049+
$ref: '#/components/responses/404'
3050+
security:
3051+
-
3052+
bearerAuth: []
30133053
'/projects/{uuid}':
30143054
get:
30153055
tags:
@@ -3041,6 +3081,39 @@ paths:
30413081
security:
30423082
-
30433083
bearerAuth: []
3084+
delete:
3085+
tags:
3086+
- Projects
3087+
summary: Delete
3088+
description: 'Delete project by UUID.'
3089+
operationId: f668a936f505b4401948c74b6a663029
3090+
parameters:
3091+
-
3092+
name: uuid
3093+
in: path
3094+
description: 'UUID of the application.'
3095+
required: true
3096+
schema:
3097+
type: string
3098+
format: uuid
3099+
responses:
3100+
'200':
3101+
description: 'Project deleted.'
3102+
content:
3103+
application/json:
3104+
schema:
3105+
properties:
3106+
message: { type: string, example: 'Project deleted.' }
3107+
type: object
3108+
'401':
3109+
$ref: '#/components/responses/401'
3110+
'400':
3111+
$ref: '#/components/responses/400'
3112+
'404':
3113+
$ref: '#/components/responses/404'
3114+
security:
3115+
-
3116+
bearerAuth: []
30443117
'/projects/{uuid}/{environment_name}':
30453118
get:
30463119
tags:
@@ -4480,6 +4553,9 @@ components:
44804553
is_container_label_escape_enabled:
44814554
type: boolean
44824555
description: 'The flag to enable the container label escape.'
4556+
is_container_label_readonly_enabled:
4557+
type: boolean
4558+
description: 'The flag to enable the container label readonly.'
44834559
config_hash:
44844560
type: string
44854561
description: 'The hash of the service configuration.'

routes/api.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
Route::get('/projects/{uuid}', [ProjectController::class, 'project_by_uuid']);
4242
Route::get('/projects/{uuid}/{environment_name}', [ProjectController::class, 'environment_details']);
4343

44+
Route::post('/projects', [ProjectController::class, 'create_project']);
45+
Route::delete('/projects/{uuid}', [ProjectController::class, 'delete_project']);
46+
4447
Route::get('/security/keys', [SecurityController::class, 'keys']);
4548
Route::post('/security/keys', [SecurityController::class, 'create_key'])->middleware([IgnoreReadOnlyApiToken::class]);
4649

0 commit comments

Comments
 (0)