Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ which also has more detailed installation instructions in the README.

## API Documentation

Visit `/docs` endpoint to access the full interactive documentation for `phpList/rest-api`.
Visit `https://phplist.github.io/restapi-docs/` endpoint to access the full interactive documentation for `phpList/rest-api`.

Look at the **"API Documentation with Swagger"** section in the [contribution guide](.github/CONTRIBUTING.md) for more information on API documenation.

Expand Down
5 changes: 5 additions & 0 deletions config/services/validators.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,8 @@ services:
autowire: true
autoconfigure: true
tags: [ 'validator.constraint_validator' ]

PhpList\Core\Domain\Identity\Validator\AttributeTypeValidator:
autowire: true
autoconfigure: true

6 changes: 6 additions & 0 deletions src/Common/EventListener/ExceptionListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Exception;
use PhpList\Core\Domain\Identity\Exception\AdminAttributeCreationException;
use PhpList\Core\Domain\Subscription\Exception\AttributeDefinitionCreationException;
use PhpList\Core\Domain\Subscription\Exception\SubscriptionCreationException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
Expand Down Expand Up @@ -42,6 +43,11 @@ public function onKernelException(ExceptionEvent $event): void
'message' => $exception->getMessage(),
], $exception->getStatusCode());
$event->setResponse($response);
} elseif ($exception instanceof AttributeDefinitionCreationException) {
$response = new JsonResponse([
'message' => $exception->getMessage(),
], $exception->getStatusCode());
$event->setResponse($response);
} elseif ($exception instanceof ValidatorException) {
$response = new JsonResponse([
'message' => $exception->getMessage(),
Expand Down
15 changes: 7 additions & 8 deletions src/Identity/Controller/AdminAttributeDefinitionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
use PhpList\RestBundle\Common\Controller\BaseController;
use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider;
use PhpList\RestBundle\Common\Validator\RequestValidator;
use PhpList\RestBundle\Identity\Request\CreateAttributeDefinitionRequest;
use PhpList\RestBundle\Identity\Request\UpdateAttributeDefinitionRequest;
use PhpList\RestBundle\Identity\Request\AdminAttributeDefinitionRequest;
use PhpList\RestBundle\Identity\Serializer\AdminAttributeDefinitionNormalizer;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\HttpFoundation\JsonResponse;
Expand Down Expand Up @@ -51,7 +50,7 @@ public function __construct(
requestBody: new OA\RequestBody(
description: 'Pass parameters to create admin attribute.',
required: true,
content: new OA\JsonContent(ref: '#/components/schemas/CreateAdminAttributeDefinitionRequest')
content: new OA\JsonContent(ref: '#/components/schemas/AdminAttributeDefinitionRequest')
),
tags: ['admin-attributes'],
parameters: [
Expand Down Expand Up @@ -87,8 +86,8 @@ public function create(Request $request): JsonResponse
{
$this->requireAuthentication($request);

/** @var CreateAttributeDefinitionRequest $definitionRequest */
$definitionRequest = $this->validator->validate($request, CreateAttributeDefinitionRequest::class);
/** @var AdminAttributeDefinitionRequest $definitionRequest */
$definitionRequest = $this->validator->validate($request, AdminAttributeDefinitionRequest::class);

$attributeDefinition = $this->definitionManager->create($definitionRequest->getDto());
$this->entityManager->flush();
Expand All @@ -107,7 +106,7 @@ public function create(Request $request): JsonResponse
requestBody: new OA\RequestBody(
description: 'Pass parameters to update admin attribute.',
required: true,
content: new OA\JsonContent(ref: '#/components/schemas/CreateAdminAttributeDefinitionRequest')
content: new OA\JsonContent(ref: '#/components/schemas/AdminAttributeDefinitionRequest')
),
tags: ['admin-attributes'],
parameters: [
Expand Down Expand Up @@ -153,8 +152,8 @@ public function update(
throw $this->createNotFoundException('Attribute definition not found.');
}

/** @var UpdateAttributeDefinitionRequest $definitionRequest */
$definitionRequest = $this->validator->validate($request, UpdateAttributeDefinitionRequest::class);
/** @var AdminAttributeDefinitionRequest $definitionRequest */
$definitionRequest = $this->validator->validate($request, AdminAttributeDefinitionRequest::class);

$attributeDefinition = $this->definitionManager->update(
attributeDefinition: $attributeDefinition,
Expand Down
15 changes: 12 additions & 3 deletions src/Identity/OpenApi/SwaggerSchemasRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace PhpList\RestBundle\Identity\OpenApi;

use OpenApi\Attributes as OA;
use PhpList\Core\Domain\Common\Model\AttributeTypeEnum;

#[OA\Schema(
schema: 'CreateAdministratorRequest',
Expand Down Expand Up @@ -96,15 +97,23 @@
type: 'object'
)]
#[OA\Schema(
schema: 'CreateAdminAttributeDefinitionRequest',
schema: 'AdminAttributeDefinitionRequest',
required: ['name'],
properties: [
new OA\Property(property: 'name', type: 'string', format: 'string', example: 'Country'),
new OA\Property(property: 'type', type: 'string', example: 'checkbox'),
new OA\Property(
property: 'type',
type: 'string',
enum: [
AttributeTypeEnum::TextLine,
AttributeTypeEnum::Hidden,
],
example: 'hidden',
nullable: true
),
new OA\Property(property: 'order', type: 'number', example: 12),
new OA\Property(property: 'default_value', type: 'string', example: 'United States'),
new OA\Property(property: 'required', type: 'boolean', example: true),
new OA\Property(property: 'table_name', type: 'string', example: 'list_attributes'),
],
type: 'object'
)]
Expand Down
3 changes: 1 addition & 2 deletions src/Identity/OpenApi/SwaggerSchemasResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@
properties: [
new OA\Property(property: 'id', type: 'integer', example: 1),
new OA\Property(property: 'name', type: 'string', example: 'Country'),
new OA\Property(property: 'type', type: 'string', example: 'select'),
new OA\Property(property: 'type', type: 'string', example: 'hidden'),
new OA\Property(property: 'list_order', type: 'integer', example: 12),
new OA\Property(property: 'default_value', type: 'string', example: 'United States'),
new OA\Property(property: 'required', type: 'boolean', example: true),
new OA\Property(property: 'table_name', type: 'string', example: 'ukcounties'),
],
type: 'object'
)]
Expand Down
57 changes: 57 additions & 0 deletions src/Identity/Request/AdminAttributeDefinitionRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace PhpList\RestBundle\Identity\Request;

use PhpList\Core\Domain\Identity\Model\Dto\AdminAttributeDefinitionDto;
use PhpList\Core\Domain\Subscription\Validator\AttributeTypeValidator;
use PhpList\RestBundle\Common\Request\RequestInterface;
use Symfony\Component\Translation\IdentityTranslator;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Exception\ValidatorException;

#[Assert\Callback('validateType')]
class AdminAttributeDefinitionRequest implements RequestInterface
{
#[Assert\NotBlank]
public string $name;

#[Assert\Choice(choices: ['hidden', 'textline'], message: 'Invalid type. Allowed values: hidden, textline.')]
public ?string $type = null;

public ?int $order = null;

public ?string $defaultValue = null;

public bool $required = false;

public function getDto(): AdminAttributeDefinitionDto
{
return new AdminAttributeDefinitionDto(
name: $this->name,
type: $this->type,
listOrder: $this->order,
defaultValue: $this->defaultValue,
required: $this->required,
);
}

public function validateType(ExecutionContextInterface $context): void
{
if ($this->type === null) {
return;
}

$validator = new AttributeTypeValidator(new IdentityTranslator());

try {
$validator->validate($this->type);
} catch (ValidatorException $e) {
$context->buildViolation($e->getMessage())
->atPath('type')
->addViolation();
}
}
}
33 changes: 0 additions & 33 deletions src/Identity/Request/CreateAttributeDefinitionRequest.php

This file was deleted.

33 changes: 0 additions & 33 deletions src/Identity/Request/UpdateAttributeDefinitionRequest.php

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@
use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Attributes as OA;
use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition;
use PhpList\Core\Domain\Subscription\Repository\SubscriberAttributeDefinitionRepository;
use PhpList\Core\Domain\Subscription\Service\Manager\AttributeDefinitionManager;
use PhpList\Core\Security\Authentication;
use PhpList\RestBundle\Common\Controller\BaseController;
use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider;
use PhpList\RestBundle\Common\Validator\RequestValidator;
use PhpList\RestBundle\Subscription\Request\CreateAttributeDefinitionRequest;
use PhpList\RestBundle\Subscription\Request\UpdateAttributeDefinitionRequest;
use PhpList\RestBundle\Subscription\Request\SubscriberAttributeDefinitionRequest;
use PhpList\RestBundle\Subscription\Serializer\AttributeDefinitionNormalizer;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/subscribers/attributes', name: 'subscriber_attribute_definition_')]
#[Route('/attributes', name: 'subscriber_attribute_definition_')]
class SubscriberAttributeDefinitionController extends BaseController
{
private AttributeDefinitionManager $definitionManager;
Expand All @@ -44,14 +44,14 @@ public function __construct(

#[Route('', name: 'create', methods: ['POST'])]
#[OA\Post(
path: '/api/v2/subscribers/attributes',
path: '/api/v2/attributes',
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
'Returns created subscriber attribute definition.',
summary: 'Create a subscriber attribute definition.',
requestBody: new OA\RequestBody(
description: 'Pass parameters to create subscriber attribute.',
required: true,
content: new OA\JsonContent(ref: '#/components/schemas/CreateSubscriberAttributeDefinitionRequest')
content: new OA\JsonContent(ref: '#/components/schemas/SubscriberAttributeDefinitionRequest')
),
tags: ['subscriber-attributes'],
parameters: [
Expand All @@ -74,6 +74,11 @@ public function __construct(
description: 'Failure',
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
),
new OA\Response(
response: 409,
description: 'Failure',
content: new OA\JsonContent(ref: '#/components/schemas/AlreadyExistsResponse')
),
new OA\Response(
response: 422,
description: 'Failure',
Expand All @@ -85,8 +90,8 @@ public function create(Request $request): JsonResponse
{
$this->requireAuthentication($request);

/** @var CreateAttributeDefinitionRequest $definitionRequest */
$definitionRequest = $this->validator->validate($request, CreateAttributeDefinitionRequest::class);
/** @var SubscriberAttributeDefinitionRequest $definitionRequest */
$definitionRequest = $this->validator->validate($request, SubscriberAttributeDefinitionRequest::class);

$attributeDefinition = $this->definitionManager->create($definitionRequest->getDto());
$this->entityManager->flush();
Expand All @@ -97,14 +102,14 @@ public function create(Request $request): JsonResponse

#[Route('/{definitionId}', name: 'update', requirements: ['definitionId' => '\d+'], methods: ['PUT'])]
#[OA\Put(
path: '/api/v2/subscribers/attributes/{definitionId}',
path: '/api/v2/attributes/{definitionId}',
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
'Returns updated subscriber attribute definition.',
summary: 'Update a subscriber attribute definition.',
requestBody: new OA\RequestBody(
description: 'Pass parameters to update subscriber attribute.',
required: true,
content: new OA\JsonContent(ref: '#/components/schemas/CreateSubscriberAttributeDefinitionRequest')
content: new OA\JsonContent(ref: '#/components/schemas/SubscriberAttributeDefinitionRequest')
),
tags: ['subscriber-attributes'],
parameters: [
Expand Down Expand Up @@ -150,8 +155,8 @@ public function update(
throw $this->createNotFoundException('Attribute definition not found.');
}

/** @var UpdateAttributeDefinitionRequest $definitionRequest */
$definitionRequest = $this->validator->validate($request, UpdateAttributeDefinitionRequest::class);
/** @var SubscriberAttributeDefinitionRequest $definitionRequest */
$definitionRequest = $this->validator->validate($request, SubscriberAttributeDefinitionRequest::class);

$attributeDefinition = $this->definitionManager->update(
attributeDefinition: $attributeDefinition,
Expand All @@ -165,7 +170,7 @@ public function update(

#[Route('/{definitionId}', name: 'delete', requirements: ['definitionId' => '\d+'], methods: ['DELETE'])]
#[OA\Delete(
path: '/api/v2/subscribers/attributes/{definitionId}',
path: '/api/v2/attributes/{definitionId}',
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
'Deletes a single subscriber attribute definition.',
summary: 'Deletes an attribute definition.',
Expand Down Expand Up @@ -220,7 +225,7 @@ public function delete(

#[Route('', name: 'get_list', methods: ['GET'])]
#[OA\Get(
path: '/api/v2/subscribers/attributes',
path: '/api/v2/attributes',
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
'Returns a JSON list of all subscriber attribute definitions.',
summary: 'Gets a list of all subscriber attribute definitions.',
Expand Down Expand Up @@ -287,7 +292,7 @@ public function getPaginated(Request $request): JsonResponse

#[Route('/{definitionId}', name: 'get_one', requirements: ['definitionId' => '\d+'], methods: ['GET'])]
#[OA\Get(
path: '/api/v2/subscribers/attributes/{definitionId}',
path: '/api/v2/attributes/{definitionId}',
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
'Returns a single attribute with specified ID.',
summary: 'Gets attribute with specified ID.',
Expand Down Expand Up @@ -344,6 +349,13 @@ public function getAttributeDefinition(
throw $this->createNotFoundException('Attribute definition not found.');
}

/** @var SubscriberAttributeDefinitionRepository $repo */
$repo = $this->entityManager->getRepository(SubscriberAttributeDefinition::class);
$hydrated = $repo->findOneByName($attributeDefinition->getName());
if ($hydrated instanceof SubscriberAttributeDefinition) {
$attributeDefinition = $hydrated;
}

return $this->json(
$this->normalizer->normalize($attributeDefinition),
Response::HTTP_OK
Expand Down
Loading
Loading