Skip to content

Commit edb22e4

Browse files
committed
Feat: ConfigController
1 parent 1bd7755 commit edb22e4

11 files changed

Lines changed: 638 additions & 0 deletions

File tree

composer.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@
126126
"type": "attribute",
127127
"prefix": "/api/v2"
128128
},
129+
"rest-api-configuration": {
130+
"resource": "@PhpListRestBundle/Configuration/Controller/",
131+
"type": "attribute",
132+
"prefix": "/api/v2"
133+
},
129134
"rest-api-analitics": {
130135
"resource": "@PhpListRestBundle/Statistics/Controller/",
131136
"type": "attribute",

config/services/controllers.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ services:
2525
autoconfigure: true
2626
public: true
2727

28+
PhpList\RestBundle\Configuration\Controller\:
29+
resource: '../src/Configuration/Controller'
30+
tags: [ 'controller.service_arguments' ]
31+
autowire: true
32+
autoconfigure: true
33+
public: true
34+
2835
PhpList\RestBundle\Statistics\Controller\:
2936
resource: '../src/Statistics/Controller'
3037
tags: [ 'controller.service_arguments' ]

config/services/validators.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ services:
3737
autoconfigure: true
3838
tags: [ 'validator.constraint_validator' ]
3939

40+
PhpList\RestBundle\Configuration\Validator\Constraint\UniqueConfigKeyValidator:
41+
autowire: true
42+
autoconfigure: true
43+
tags: [ 'validator.constraint_validator' ]
44+
4045
PhpList\RestBundle\Subscription\Validator\Constraint\ListExistsValidator:
4146
autowire: true
4247
autoconfigure: true
Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Configuration\Controller;
6+
7+
use Doctrine\ORM\EntityManagerInterface;
8+
use OpenApi\Attributes as OA;
9+
use PhpList\Core\Domain\Configuration\Exception\ConfigNotEditableException;
10+
use PhpList\Core\Domain\Configuration\Model\Config;
11+
use PhpList\Core\Domain\Configuration\Service\Manager\ConfigManager;
12+
use PhpList\Core\Domain\Identity\Model\PrivilegeFlag;
13+
use PhpList\Core\Security\Authentication;
14+
use PhpList\RestBundle\Common\Controller\BaseController;
15+
use PhpList\RestBundle\Common\Validator\RequestValidator;
16+
use PhpList\RestBundle\Configuration\Request\ConfigRequest;
17+
use PhpList\RestBundle\Configuration\Request\UpdateConfigRequest;
18+
use PhpList\RestBundle\Configuration\Serializer\ConfigNormalizer;
19+
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
20+
use Symfony\Component\HttpFoundation\JsonResponse;
21+
use Symfony\Component\HttpFoundation\Request;
22+
use Symfony\Component\HttpFoundation\Response;
23+
use Symfony\Component\Routing\Attribute\Route;
24+
25+
#[Route('/configs', name: 'config_')]
26+
class ConfigController extends BaseController
27+
{
28+
public function __construct(
29+
Authentication $authentication,
30+
RequestValidator $validator,
31+
private readonly ConfigManager $manager,
32+
private readonly ConfigNormalizer $normalizer,
33+
private readonly EntityManagerInterface $entityManager,
34+
) {
35+
parent::__construct($authentication, $validator);
36+
}
37+
38+
#[Route('', name: 'get_list', methods: ['GET'])]
39+
#[OA\Get(
40+
path: '/api/v2/config',
41+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
42+
'Returns all configuration items.',
43+
summary: 'Gets all configuration items.',
44+
tags: ['config'],
45+
parameters: [
46+
new OA\Parameter(
47+
name: 'php-auth-pw',
48+
description: 'Session key obtained from login',
49+
in: 'header',
50+
required: true,
51+
schema: new OA\Schema(type: 'string')
52+
),
53+
],
54+
responses: [
55+
new OA\Response(
56+
response: 200,
57+
description: 'Success',
58+
content: new OA\JsonContent(
59+
properties: [
60+
new OA\Property(
61+
property: 'items',
62+
type: 'array',
63+
items: new OA\Items(ref: '#/components/schemas/Config')
64+
),
65+
new OA\Property(property: 'pagination', ref: '#/components/schemas/CursorPagination')
66+
],
67+
type: 'object'
68+
)
69+
),
70+
new OA\Response(
71+
response: 403,
72+
description: 'Failure',
73+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
74+
),
75+
]
76+
)]
77+
public function list(Request $request): JsonResponse
78+
{
79+
$this->denyUnlessSettingsAdmin($request, 'You are not allowed to view configuration.');
80+
$items = $this->manager->getAllEditable();
81+
82+
usort(
83+
$items,
84+
fn (Config $aConf, Config $bConf): int => strcmp(
85+
strtolower($aConf->getKey()),
86+
strtolower($bConf->getKey())
87+
)
88+
);
89+
90+
$count = count($items);
91+
92+
return $this->json(
93+
data: [
94+
'items' => array_map(fn($config) => $this->normalizer->normalize($config), $items),
95+
'pagination' => [
96+
'total' => $count,
97+
'limit' => $count + 1,
98+
'has_more' => false,
99+
'next_cursor' => null
100+
]
101+
],
102+
status: Response::HTTP_OK
103+
);
104+
}
105+
106+
#[Route('/{key}', name: 'get_one', requirements: ['key' => '[A-Za-z0-9_.:-]+'], methods: ['GET'])]
107+
#[OA\Get(
108+
path: '/api/v2/config/{key}',
109+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
110+
'Returns one configuration item by key.',
111+
summary: 'Gets a configuration item.',
112+
tags: ['config'],
113+
parameters: [
114+
new OA\Parameter(
115+
name: 'php-auth-pw',
116+
description: 'Session key obtained from login',
117+
in: 'header',
118+
required: true,
119+
schema: new OA\Schema(type: 'string')
120+
),
121+
new OA\Parameter(
122+
name: 'key',
123+
description: 'Configuration key',
124+
in: 'path',
125+
required: true,
126+
schema: new OA\Schema(type: 'string')
127+
),
128+
],
129+
responses: [
130+
new OA\Response(
131+
response: 200,
132+
description: 'Success',
133+
content: new OA\JsonContent(ref: '#/components/schemas/Config')
134+
),
135+
new OA\Response(
136+
response: 403,
137+
description: 'Failure',
138+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
139+
),
140+
new OA\Response(
141+
response: 404,
142+
description: 'Failure',
143+
content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse')
144+
),
145+
]
146+
)]
147+
public function getOne(
148+
Request $request,
149+
#[MapEntity(mapping: ['key' => 'key'])] ?Config $config,
150+
): JsonResponse {
151+
$this->denyUnlessSettingsAdmin($request, 'You are not allowed to view configuration.');
152+
if ($config === null) {
153+
throw $this->createNotFoundException('Configuration item not found.');
154+
}
155+
156+
return $this->json($this->normalizer->normalize($config), Response::HTTP_OK);
157+
}
158+
159+
#[Route('', name: 'create', methods: ['POST'])]
160+
#[OA\Post(
161+
path: '/api/v2/config',
162+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
163+
'Creates a configuration item.',
164+
summary: 'Creates a configuration item.',
165+
requestBody: new OA\RequestBody(
166+
description: 'Configuration item data',
167+
required: true,
168+
content: new OA\JsonContent(ref: '#/components/schemas/ConfigRequest')
169+
),
170+
tags: ['config'],
171+
parameters: [
172+
new OA\Parameter(
173+
name: 'php-auth-pw',
174+
description: 'Session key obtained from login',
175+
in: 'header',
176+
required: true,
177+
schema: new OA\Schema(type: 'string')
178+
),
179+
],
180+
responses: [
181+
new OA\Response(
182+
response: 201,
183+
description: 'Success',
184+
content: new OA\JsonContent(ref: '#/components/schemas/Config')
185+
),
186+
new OA\Response(
187+
response: 403,
188+
description: 'Failure',
189+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
190+
),
191+
new OA\Response(
192+
response: 409,
193+
description: 'Failure',
194+
content: new OA\JsonContent(ref: '#/components/schemas/AlreadyExistsResponse')
195+
),
196+
new OA\Response(
197+
response: 422,
198+
description: 'Failure',
199+
content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse')
200+
),
201+
]
202+
)]
203+
public function create(Request $request): JsonResponse
204+
{
205+
$this->denyUnlessSettingsAdmin($request, 'You are not allowed to create configuration.');
206+
/* @var ConfigRequest $configRequest */
207+
$configRequest = $this->validator->validate($request, ConfigRequest::class);
208+
209+
$config = $this->manager->create($configRequest->getDto());
210+
$this->entityManager->flush();
211+
212+
return $this->json($this->normalizer->normalize($config), Response::HTTP_CREATED);
213+
}
214+
215+
#[Route('/{key}', name: 'update', requirements: ['key' => '[A-Za-z0-9_.:-]+'], methods: ['PUT'])]
216+
#[OA\Put(
217+
path: '/api/v2/config/{key}',
218+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
219+
'Updates a configuration item value.',
220+
summary: 'Updates a configuration item.',
221+
requestBody: new OA\RequestBody(
222+
description: 'Configuration item data',
223+
required: true,
224+
content: new OA\JsonContent(ref: '#/components/schemas/ConfigUpdateRequest')
225+
),
226+
tags: ['config'],
227+
parameters: [
228+
new OA\Parameter(
229+
name: 'php-auth-pw',
230+
description: 'Session key obtained from login',
231+
in: 'header',
232+
required: true,
233+
schema: new OA\Schema(type: 'string')
234+
),
235+
new OA\Parameter(
236+
name: 'key',
237+
description: 'Configuration key',
238+
in: 'path',
239+
required: true,
240+
schema: new OA\Schema(type: 'string')
241+
),
242+
],
243+
responses: [
244+
new OA\Response(
245+
response: 200,
246+
description: 'Success',
247+
content: new OA\JsonContent(ref: '#/components/schemas/Config')
248+
),
249+
new OA\Response(
250+
response: 403,
251+
description: 'Failure',
252+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
253+
),
254+
new OA\Response(
255+
response: 404,
256+
description: 'Failure',
257+
content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse')
258+
),
259+
new OA\Response(
260+
response: 422,
261+
description: 'Failure',
262+
content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse')
263+
),
264+
]
265+
)]
266+
public function update(
267+
Request $request,
268+
#[MapEntity(mapping: ['key' => 'key'])] ?Config $config = null
269+
): JsonResponse {
270+
$this->denyUnlessSettingsAdmin($request, 'You are not allowed to update configuration.');
271+
if ($config === null) {
272+
throw $this->createNotFoundException('Configuration item not found.');
273+
}
274+
/* @var UpdateConfigRequest $dto */
275+
$dto = $this->validator->validate($request, UpdateConfigRequest::class);
276+
277+
try {
278+
$this->manager->update($config, $dto->getDto()['value']);
279+
} catch (ConfigNotEditableException $exception) {
280+
throw $this->createAccessDeniedException($exception->getMessage());
281+
}
282+
$this->entityManager->flush();
283+
284+
return $this->json($this->normalizer->normalize($config), Response::HTTP_OK);
285+
}
286+
287+
#[Route('/{key}', name: 'delete', requirements: ['key' => '[A-Za-z0-9_.:-]+'], methods: ['DELETE'])]
288+
#[OA\Delete(
289+
path: '/api/v2/config/{key}',
290+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
291+
'Deletes a configuration item.',
292+
summary: 'Deletes a configuration item.',
293+
tags: ['config'],
294+
parameters: [
295+
new OA\Parameter(
296+
name: 'php-auth-pw',
297+
description: 'Session key obtained from login',
298+
in: 'header',
299+
required: true,
300+
schema: new OA\Schema(type: 'string')
301+
),
302+
new OA\Parameter(
303+
name: 'key',
304+
description: 'Configuration key',
305+
in: 'path',
306+
required: true,
307+
schema: new OA\Schema(type: 'string')
308+
),
309+
],
310+
responses: [
311+
new OA\Response(response: Response::HTTP_NO_CONTENT, description: 'Success'),
312+
new OA\Response(
313+
response: 403,
314+
description: 'Failure',
315+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
316+
),
317+
new OA\Response(
318+
response: 404,
319+
description: 'Failure',
320+
content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse')
321+
),
322+
]
323+
)]
324+
public function delete(
325+
Request $request,
326+
#[MapEntity(mapping: ['key' => 'key'])] ?Config $config = null
327+
): JsonResponse {
328+
$this->denyUnlessSettingsAdmin($request, 'You are not allowed to delete configuration.');
329+
if ($config === null) {
330+
throw $this->createNotFoundException('Configuration item not found.');
331+
}
332+
333+
$this->manager->delete($config);
334+
$this->entityManager->flush();
335+
336+
return $this->json(null, Response::HTTP_NO_CONTENT);
337+
}
338+
339+
private function denyUnlessSettingsAdmin(Request $request, string $message): void
340+
{
341+
$admin = $this->requireAuthentication($request);
342+
if (!$admin->getPrivileges()->has(PrivilegeFlag::Settings)) {
343+
throw $this->createAccessDeniedException($message);
344+
}
345+
}
346+
}

0 commit comments

Comments
 (0)