From ab87412626c3638b39d0fa4af63df3567a52fa99 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Tue, 6 May 2025 22:10:43 +0400 Subject: [PATCH 01/21] ISSUE-345: move logic to core --- composer.json | 2 +- config/services/builders.yml | 10 +- config/services/managers.yml | 16 +- config/services/validators.yml | 4 +- src/Controller/AdministratorController.php | 15 +- src/Controller/CampaignController.php | 8 +- src/Controller/ListController.php | 4 +- src/Controller/ListMembersController.php | 2 +- src/Controller/SessionController.php | 7 +- src/Controller/SubscriberController.php | 10 +- src/Controller/SubscriptionController.php | 2 +- src/Controller/TemplateController.php | 4 +- src/Entity/Dto/MessageContext.php | 25 --- .../Request/CreateAdministratorRequest.php | 11 ++ src/Entity/Request/CreateMessageRequest.php | 14 ++ src/Entity/Request/CreateSessionRequest.php | 5 + .../Request/CreateSubscriberListRequest.php | 11 ++ .../Request/CreateSubscriberRequest.php | 10 ++ src/Entity/Request/CreateTemplateRequest.php | 14 ++ .../Request/Message/MessageContentRequest.php | 11 ++ .../Request/Message/MessageFormatRequest.php | 10 ++ .../Message/MessageMetadataRequest.php | 8 + .../Request/Message/MessageOptionsRequest.php | 11 ++ .../Message/MessageScheduleRequest.php | 12 ++ src/Entity/Request/RequestInterface.php | 2 +- src/Entity/Request/SubscriptionRequest.php | 5 + .../Request/UpdateAdministratorRequest.php | 14 +- src/Entity/Request/UpdateMessageRequest.php | 16 ++ .../Request/UpdateSubscriberRequest.php | 14 ++ src/EventListener/ExceptionListener.php | 2 +- .../SubscriptionCreationException.php | 23 --- .../Builder/BuilderFromDtoInterface.php | 11 -- .../Builder/BuilderFromRequestInterface.php | 11 -- src/Service/Builder/MessageBuilder.php | 56 ------- src/Service/Builder/MessageContentBuilder.php | 26 --- src/Service/Builder/MessageFormatBuilder.php | 25 --- src/Service/Builder/MessageOptionsBuilder.php | 27 ---- .../Builder/MessageScheduleBuilder.php | 28 ---- src/Service/Manager/AdministratorManager.php | 63 -------- src/Service/Manager/MessageManager.php | 57 ------- src/Service/Manager/SessionManager.php | 49 ------ src/Service/Manager/SubscriberListManager.php | 55 ------- src/Service/Manager/SubscriberManager.php | 72 --------- src/Service/Manager/SubscriptionManager.php | 90 ----------- src/Service/Manager/TemplateImageManager.php | 104 ------------ src/Service/Manager/TemplateManager.php | 88 ---------- .../Provider/PaginatedDataProvider.php | 2 +- src/Validator/TemplateImageValidator.php | 72 --------- src/Validator/TemplateLinkValidator.php | 62 ------- src/Validator/ValidatorInterface.php | 12 -- tests/Helpers/DummyPaginatableRepository.php | 2 +- .../EventListener/ExceptionListenerTest.php | 5 +- .../Service/Builder/MessageBuilderTest.php | 148 ----------------- .../Builder/MessageContentBuilderTest.php | 44 ----- .../Builder/MessageFormatBuilderTest.php | 42 ----- .../Builder/MessageOptionsBuilderTest.php | 44 ----- .../Builder/MessageScheduleBuilderTest.php | 47 ------ .../Manager/AdministratorManagerTest.php | 94 ----------- .../Service/Manager/MessageManagerTest.php | 152 ------------------ .../Service/Manager/SessionManagerTest.php | 54 ------- .../Manager/SubscriberListManagerTest.php | 77 --------- .../Service/Manager/SubscriberManagerTest.php | 47 ------ .../Manager/SubscriptionManagerTest.php | 107 ------------ .../Manager/TemplateImageManagerTest.php | 88 ---------- .../Service/Manager/TemplateManagerTest.php | 90 ----------- .../Validator/TemplateImageValidatorTest.php | 86 ---------- .../Validator/TemplateLinkValidatorTest.php | 66 -------- 67 files changed, 216 insertions(+), 2189 deletions(-) delete mode 100644 src/Entity/Dto/MessageContext.php delete mode 100644 src/Exception/SubscriptionCreationException.php delete mode 100644 src/Service/Builder/BuilderFromDtoInterface.php delete mode 100644 src/Service/Builder/BuilderFromRequestInterface.php delete mode 100644 src/Service/Builder/MessageBuilder.php delete mode 100644 src/Service/Builder/MessageContentBuilder.php delete mode 100644 src/Service/Builder/MessageFormatBuilder.php delete mode 100644 src/Service/Builder/MessageOptionsBuilder.php delete mode 100644 src/Service/Builder/MessageScheduleBuilder.php delete mode 100644 src/Service/Manager/AdministratorManager.php delete mode 100644 src/Service/Manager/MessageManager.php delete mode 100644 src/Service/Manager/SessionManager.php delete mode 100644 src/Service/Manager/SubscriberListManager.php delete mode 100644 src/Service/Manager/SubscriberManager.php delete mode 100644 src/Service/Manager/SubscriptionManager.php delete mode 100644 src/Service/Manager/TemplateImageManager.php delete mode 100644 src/Service/Manager/TemplateManager.php delete mode 100644 src/Validator/TemplateImageValidator.php delete mode 100644 src/Validator/TemplateLinkValidator.php delete mode 100644 src/Validator/ValidatorInterface.php delete mode 100644 tests/Unit/Service/Builder/MessageBuilderTest.php delete mode 100644 tests/Unit/Service/Builder/MessageContentBuilderTest.php delete mode 100644 tests/Unit/Service/Builder/MessageFormatBuilderTest.php delete mode 100644 tests/Unit/Service/Builder/MessageOptionsBuilderTest.php delete mode 100644 tests/Unit/Service/Builder/MessageScheduleBuilderTest.php delete mode 100644 tests/Unit/Service/Manager/AdministratorManagerTest.php delete mode 100644 tests/Unit/Service/Manager/MessageManagerTest.php delete mode 100644 tests/Unit/Service/Manager/SessionManagerTest.php delete mode 100644 tests/Unit/Service/Manager/SubscriberListManagerTest.php delete mode 100644 tests/Unit/Service/Manager/SubscriberManagerTest.php delete mode 100644 tests/Unit/Service/Manager/SubscriptionManagerTest.php delete mode 100644 tests/Unit/Service/Manager/TemplateImageManagerTest.php delete mode 100644 tests/Unit/Service/Manager/TemplateManagerTest.php delete mode 100644 tests/Unit/Validator/TemplateImageValidatorTest.php delete mode 100644 tests/Unit/Validator/TemplateLinkValidatorTest.php diff --git a/composer.json b/composer.json index 1d4c9f0..06c8ac9 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require": { "php": "^8.1", - "phplist/core": "v5.0.0-alpha6", + "phplist/core": "dev-ISSUE-345", "friendsofsymfony/rest-bundle": "*", "symfony/test-pack": "^1.0", "symfony/process": "^6.4", diff --git a/config/services/builders.yml b/config/services/builders.yml index e37a342..655e3d6 100644 --- a/config/services/builders.yml +++ b/config/services/builders.yml @@ -4,22 +4,22 @@ services: autoconfigure: true public: false - PhpList\RestBundle\Service\Builder\MessageBuilder: + PhpList\Core\Domain\Service\Builder\MessageBuilder: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Builder\MessageFormatBuilder: + PhpList\Core\Domain\Service\Builder\MessageFormatBuilder: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Builder\MessageScheduleBuilder: + PhpList\Core\Domain\Service\Builder\MessageScheduleBuilder: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Builder\MessageContentBuilder: + PhpList\Core\Domain\Service\Builder\MessageContentBuilder: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Builder\MessageOptionsBuilder: + PhpList\Core\Domain\Service\Builder\MessageOptionsBuilder: autowire: true autoconfigure: true diff --git a/config/services/managers.yml b/config/services/managers.yml index 7f42416..1bf9380 100644 --- a/config/services/managers.yml +++ b/config/services/managers.yml @@ -4,34 +4,34 @@ services: autoconfigure: true public: false - PhpList\RestBundle\Service\Manager\SubscriberManager: + PhpList\Core\Domain\Service\Manager\SubscriberManager: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Manager\SessionManager: + PhpList\Core\Domain\Service\Manager\SessionManager: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Manager\SubscriberListManager: + PhpList\Core\Domain\Service\Manager\SubscriberListManager: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Manager\SubscriptionManager: + PhpList\Core\Domain\Service\Manager\SubscriptionManager: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Manager\MessageManager: + PhpList\Core\Domain\Service\Manager\MessageManager: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Manager\TemplateManager: + PhpList\Core\Domain\Service\Manager\TemplateManager: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Manager\TemplateImageManager: + PhpList\Core\Domain\Service\Manager\TemplateImageManager: autowire: true autoconfigure: true - PhpList\RestBundle\Service\Manager\AdministratorManager: + PhpList\Core\Domain\Service\Manager\AdministratorManager: autowire: true autoconfigure: true diff --git a/config/services/validators.yml b/config/services/validators.yml index d1d519c..2579b6a 100644 --- a/config/services/validators.yml +++ b/config/services/validators.yml @@ -19,11 +19,11 @@ services: autoconfigure: true tags: [ 'validator.constraint_validator' ] - PhpList\RestBundle\Validator\TemplateLinkValidator: + PhpList\Core\Domain\Service\Validator\TemplateLinkValidator: autowire: true autoconfigure: true - PhpList\RestBundle\Validator\TemplateImageValidator: + PhpList\Core\Domain\Service\Validator\TemplateImageValidator: autowire: true autoconfigure: true diff --git a/src/Controller/AdministratorController.php b/src/Controller/AdministratorController.php index 1adb2be..208e420 100644 --- a/src/Controller/AdministratorController.php +++ b/src/Controller/AdministratorController.php @@ -6,11 +6,11 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Model\Identity\Administrator; +use PhpList\Core\Domain\Service\Manager\AdministratorManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateAdministratorRequest; use PhpList\RestBundle\Entity\Request\UpdateAdministratorRequest; use PhpList\RestBundle\Serializer\AdministratorNormalizer; -use PhpList\RestBundle\Service\Manager\AdministratorManager; use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; use PhpList\RestBundle\Validator\RequestValidator; use Symfony\Bridge\Doctrine\Attribute\MapEntity; @@ -135,9 +135,10 @@ public function createAdministrator( ): JsonResponse { $this->requireAuthentication($request); - /** @var CreateAdministratorRequest $dto */ - $dto = $validator->validate($request, CreateAdministratorRequest::class); - $administrator = $this->administratorManager->createAdministrator($dto); + /** @var CreateAdministratorRequest $createAdministratorRequest */ + $createAdministratorRequest = $validator->validate($request, CreateAdministratorRequest::class); + + $administrator = $this->administratorManager->createAdministrator($createAdministratorRequest->getDto()); $json = $normalizer->normalize($administrator, 'json'); return $this->json($json, Response::HTTP_CREATED); @@ -224,9 +225,9 @@ public function updateAdministrator( if (!$administrator) { throw $this->createNotFoundException('Administrator not found.'); } - /** @var UpdateAdministratorRequest $dto */ - $dto = $this->validator->validate($request, UpdateAdministratorRequest::class); - $this->administratorManager->updateAdministrator($administrator, $dto); + /** @var UpdateAdministratorRequest $updateAdministratorRequest */ + $updateAdministratorRequest = $this->validator->validate($request, UpdateAdministratorRequest::class); + $this->administratorManager->updateAdministrator($administrator, $updateAdministratorRequest->getDto()); return $this->json(null, Response::HTTP_OK); } diff --git a/src/Controller/CampaignController.php b/src/Controller/CampaignController.php index 73789bb..071a446 100644 --- a/src/Controller/CampaignController.php +++ b/src/Controller/CampaignController.php @@ -5,13 +5,13 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Filter\MessageFilter; +use PhpList\Core\Domain\Model\Dto\Filter\MessageFilter; use PhpList\Core\Domain\Model\Messaging\Message; +use PhpList\Core\Domain\Service\Manager\MessageManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateMessageRequest; use PhpList\RestBundle\Entity\Request\UpdateMessageRequest; use PhpList\RestBundle\Serializer\MessageNormalizer; -use PhpList\RestBundle\Service\Manager\MessageManager; use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; use PhpList\RestBundle\Validator\RequestValidator; use Symfony\Bridge\Doctrine\Attribute\MapEntity; @@ -218,7 +218,7 @@ public function createMessage(Request $request, MessageNormalizer $normalizer): /** @var CreateMessageRequest $createMessageRequest */ $createMessageRequest = $this->validator->validate($request, CreateMessageRequest::class); - $data = $this->messageManager->createMessage($createMessageRequest, $authUser); + $data = $this->messageManager->createMessage($createMessageRequest->getDto(), $authUser); return $this->json($normalizer->normalize($data), Response::HTTP_CREATED); } @@ -291,7 +291,7 @@ public function updateMessage( } /** @var UpdateMessageRequest $updateMessageRequest */ $updateMessageRequest = $this->validator->validate($request, UpdateMessageRequest::class); - $data = $this->messageManager->updateMessage($updateMessageRequest, $message, $authUser); + $data = $this->messageManager->updateMessage($updateMessageRequest->getDto(), $message, $authUser); return $this->json($this->normalizer->normalize($data), Response::HTTP_OK); } diff --git a/src/Controller/ListController.php b/src/Controller/ListController.php index c7af395..af9f9b3 100644 --- a/src/Controller/ListController.php +++ b/src/Controller/ListController.php @@ -6,10 +6,10 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Model\Subscription\SubscriberList; +use PhpList\Core\Domain\Service\Manager\SubscriberListManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateSubscriberListRequest; use PhpList\RestBundle\Serializer\SubscriberListNormalizer; -use PhpList\RestBundle\Service\Manager\SubscriberListManager; use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; use PhpList\RestBundle\Validator\RequestValidator; use Symfony\Bridge\Doctrine\Attribute\MapEntity; @@ -279,7 +279,7 @@ public function createList(Request $request, SubscriberListNormalizer $normalize /** @var CreateSubscriberListRequest $subscriberListRequest */ $subscriberListRequest = $this->validator->validate($request, CreateSubscriberListRequest::class); - $data = $this->subscriberListManager->createSubscriberList($subscriberListRequest, $authUser); + $data = $this->subscriberListManager->createSubscriberList($subscriberListRequest->getDto(), $authUser); return $this->json($normalizer->normalize($data), Response::HTTP_CREATED); } diff --git a/src/Controller/ListMembersController.php b/src/Controller/ListMembersController.php index f499a82..0fae297 100644 --- a/src/Controller/ListMembersController.php +++ b/src/Controller/ListMembersController.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Filter\SubscriberFilter; +use PhpList\Core\Domain\Model\Dto\Filter\SubscriberFilter; use PhpList\Core\Domain\Model\Subscription\Subscriber; use PhpList\Core\Domain\Model\Subscription\SubscriberList; use PhpList\Core\Security\Authentication; diff --git a/src/Controller/SessionController.php b/src/Controller/SessionController.php index 9755d69..879c045 100644 --- a/src/Controller/SessionController.php +++ b/src/Controller/SessionController.php @@ -6,10 +6,10 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Model\Identity\AdministratorToken; +use PhpList\Core\Domain\Service\Manager\SessionManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateSessionRequest; use PhpList\RestBundle\Serializer\AdministratorTokenNormalizer; -use PhpList\RestBundle\Service\Manager\SessionManager; use PhpList\RestBundle\Validator\RequestValidator; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; @@ -90,7 +90,10 @@ public function createSession( ): JsonResponse { /** @var CreateSessionRequest $createSessionRequest */ $createSessionRequest = $this->validator->validate($request, CreateSessionRequest::class); - $token = $this->sessionManager->createSession($createSessionRequest); + $token = $this->sessionManager->createSession( + $createSessionRequest->loginName, + $createSessionRequest->password + ); $json = $normalizer->normalize($token, 'json'); diff --git a/src/Controller/SubscriberController.php b/src/Controller/SubscriberController.php index 7ed809d..b233fc7 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Controller/SubscriberController.php @@ -6,11 +6,11 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Model\Subscription\Subscriber; +use PhpList\Core\Domain\Service\Manager\SubscriberManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateSubscriberRequest; use PhpList\RestBundle\Entity\Request\UpdateSubscriberRequest; use PhpList\RestBundle\Serializer\SubscriberNormalizer; -use PhpList\RestBundle\Service\Manager\SubscriberManager; use PhpList\RestBundle\Validator\RequestValidator; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; @@ -98,7 +98,7 @@ public function createSubscriber(Request $request): JsonResponse /** @var CreateSubscriberRequest $subscriberRequest */ $subscriberRequest = $this->validator->validate($request, CreateSubscriberRequest::class); - $subscriber = $this->subscriberManager->createSubscriber($subscriberRequest); + $subscriber = $this->subscriberManager->createSubscriber($subscriberRequest->getDto()); return $this->json( $this->subscriberNormalizer->normalize($subscriber, 'json'), @@ -175,9 +175,9 @@ public function updateSubscriber( if (!$subscriber) { throw $this->createNotFoundException('Subscriber not found.'); } - /** @var UpdateSubscriberRequest $dto */ - $dto = $this->validator->validate($request, UpdateSubscriberRequest::class); - $subscriber = $this->subscriberManager->updateSubscriber($dto); + /** @var UpdateSubscriberRequest $updateSubscriberRequest */ + $updateSubscriberRequest = $this->validator->validate($request, UpdateSubscriberRequest::class); + $subscriber = $this->subscriberManager->updateSubscriber($updateSubscriberRequest->getDto()); return $this->json($this->subscriberNormalizer->normalize($subscriber, 'json'), Response::HTTP_OK); } diff --git a/src/Controller/SubscriptionController.php b/src/Controller/SubscriptionController.php index ea2dacc..2bf4e8b 100644 --- a/src/Controller/SubscriptionController.php +++ b/src/Controller/SubscriptionController.php @@ -6,10 +6,10 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Model\Subscription\SubscriberList; +use PhpList\Core\Domain\Service\Manager\SubscriptionManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\SubscriptionRequest; use PhpList\RestBundle\Serializer\SubscriptionNormalizer; -use PhpList\RestBundle\Service\Manager\SubscriptionManager; use PhpList\RestBundle\Validator\RequestValidator; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; diff --git a/src/Controller/TemplateController.php b/src/Controller/TemplateController.php index 7914dd3..f9e8d42 100644 --- a/src/Controller/TemplateController.php +++ b/src/Controller/TemplateController.php @@ -6,10 +6,10 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Model\Messaging\Template; +use PhpList\Core\Domain\Service\Manager\TemplateManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateTemplateRequest; use PhpList\RestBundle\Serializer\TemplateNormalizer; -use PhpList\RestBundle\Service\Manager\TemplateManager; use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; use PhpList\RestBundle\Validator\RequestValidator; use Symfony\Bridge\Doctrine\Attribute\MapEntity; @@ -264,7 +264,7 @@ public function createTemplates(Request $request): JsonResponse $createTemplateRequest = $this->validator->validate($request, CreateTemplateRequest::class); return $this->json( - $this->normalizer->normalize($this->templateManager->create($createTemplateRequest)), + $this->normalizer->normalize($this->templateManager->create($createTemplateRequest->getDto())), Response::HTTP_CREATED ); } diff --git a/src/Entity/Dto/MessageContext.php b/src/Entity/Dto/MessageContext.php deleted file mode 100644 index 594834a..0000000 --- a/src/Entity/Dto/MessageContext.php +++ /dev/null @@ -1,25 +0,0 @@ -user; - } - - public function getExisting(): ?Message - { - return $this->existing; - } -} diff --git a/src/Entity/Request/CreateAdministratorRequest.php b/src/Entity/Request/CreateAdministratorRequest.php index 71bf9d6..7d6ad2a 100644 --- a/src/Entity/Request/CreateAdministratorRequest.php +++ b/src/Entity/Request/CreateAdministratorRequest.php @@ -7,6 +7,7 @@ use PhpList\Core\Domain\Model\Identity\Administrator; use Symfony\Component\Validator\Constraints as Assert; use PhpList\RestBundle\Validator\Constraint as CustomAssert; +use PhpList\Core\Domain\Model\Identity\Dto\CreateAdministratorDto; class CreateAdministratorRequest implements RequestInterface { @@ -27,4 +28,14 @@ class CreateAdministratorRequest implements RequestInterface #[Assert\NotNull] #[Assert\Type('bool')] public bool $superUser = false; + + public function getDto(): CreateAdministratorDto + { + return new CreateAdministratorDto( + $this->loginName, + $this->password, + $this->email, + $this->superUser + ); + } } diff --git a/src/Entity/Request/CreateMessageRequest.php b/src/Entity/Request/CreateMessageRequest.php index 0aaf51c..c9621d7 100644 --- a/src/Entity/Request/CreateMessageRequest.php +++ b/src/Entity/Request/CreateMessageRequest.php @@ -4,6 +4,8 @@ namespace PhpList\RestBundle\Entity\Request; +use PhpList\Core\Domain\Model\Messaging\Dto\CreateMessageDto; +use PhpList\Core\Domain\Model\Messaging\Dto\MessageDtoInterface; use PhpList\RestBundle\Entity\Request\Message\MessageContentRequest; use PhpList\RestBundle\Entity\Request\Message\MessageFormatRequest; use PhpList\RestBundle\Entity\Request\Message\MessageMetadataRequest; @@ -36,4 +38,16 @@ class CreateMessageRequest implements RequestInterface #[CustomAssert\TemplateExists] public ?int $templateId; + + public function getDto(): MessageDtoInterface + { + return new CreateMessageDto( + content: $this->content->getDto(), + format: $this->format->getDto(), + metadata: $this->metadata->getDto(), + options: $this->options->getDto(), + schedule: $this->schedule->getDto(), + templateId: $this->templateId, + ); + } } diff --git a/src/Entity/Request/CreateSessionRequest.php b/src/Entity/Request/CreateSessionRequest.php index 9291358..6bb984b 100644 --- a/src/Entity/Request/CreateSessionRequest.php +++ b/src/Entity/Request/CreateSessionRequest.php @@ -15,4 +15,9 @@ class CreateSessionRequest implements RequestInterface #[Assert\NotBlank] #[Assert\Type(type: 'string')] public string $password; + + public function getDto(): CreateSessionRequest + { + return $this; + } } diff --git a/src/Entity/Request/CreateSubscriberListRequest.php b/src/Entity/Request/CreateSubscriberListRequest.php index 395141c..7857f44 100644 --- a/src/Entity/Request/CreateSubscriberListRequest.php +++ b/src/Entity/Request/CreateSubscriberListRequest.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Entity\Request; +use PhpList\Core\Domain\Model\Subscription\Dto\CreateSubscriberListDto; use Symfony\Component\Validator\Constraints as Assert; class CreateSubscriberListRequest implements RequestInterface @@ -17,4 +18,14 @@ class CreateSubscriberListRequest implements RequestInterface public ?int $listPosition = null; public ?string $description = null; + + public function getDto(): CreateSubscriberListDto + { + return new CreateSubscriberListDto( + name: $this->name, + isPublic: $this->public, + listPosition: $this->listPosition, + description: $this->description, + ); + } } diff --git a/src/Entity/Request/CreateSubscriberRequest.php b/src/Entity/Request/CreateSubscriberRequest.php index 87f5b9e..6112fdb 100644 --- a/src/Entity/Request/CreateSubscriberRequest.php +++ b/src/Entity/Request/CreateSubscriberRequest.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Entity\Request; +use PhpList\Core\Domain\Model\Subscription\Dto\CreateSubscriberDto; use PhpList\Core\Domain\Model\Subscription\Subscriber; use PhpList\RestBundle\Validator\Constraint as CustomAssert; use Symfony\Component\Validator\Constraints as Assert; @@ -20,4 +21,13 @@ class CreateSubscriberRequest implements RequestInterface #[Assert\Type(type: 'bool')] public ?bool $htmlEmail = null; + + public function getDto(): CreateSubscriberDto + { + return new CreateSubscriberDto( + email: $this->email, + requestConfirmation: $this->requestConfirmation, + htmlEmail: $this->htmlEmail, + ); + } } diff --git a/src/Entity/Request/CreateTemplateRequest.php b/src/Entity/Request/CreateTemplateRequest.php index 3ba90bf..3f8a988 100644 --- a/src/Entity/Request/CreateTemplateRequest.php +++ b/src/Entity/Request/CreateTemplateRequest.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Entity\Request; +use PhpList\Core\Domain\Model\Messaging\Dto\CreateTemplateDto; use PhpList\RestBundle\Validator\Constraint as CustomAssert; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\Validator\Constraints as Assert; @@ -25,4 +26,17 @@ class CreateTemplateRequest implements RequestInterface public bool $checkLinks = false; public bool $checkImages = false; public bool $checkExternalImages = false; + + public function getDto(): CreateTemplateDto + { + return new CreateTemplateDto( + title: $this->title, + content: $this->content, + text: $this->text, + fileContent: $this->file instanceof UploadedFile ? file_get_contents($this->file->getPathname()) : null, + shouldCheckLinks: $this->checkLinks, + shouldCheckImages: $this->checkImages, + shouldCheckExternalImages: $this->checkExternalImages, + ); + } } diff --git a/src/Entity/Request/Message/MessageContentRequest.php b/src/Entity/Request/Message/MessageContentRequest.php index 2e94c0f..28ce0a2 100644 --- a/src/Entity/Request/Message/MessageContentRequest.php +++ b/src/Entity/Request/Message/MessageContentRequest.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Entity\Request\Message; +use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageContentDto; use Symfony\Component\Validator\Constraints as Assert; class MessageContentRequest implements RequestDtoInterface @@ -19,4 +20,14 @@ class MessageContentRequest implements RequestDtoInterface #[Assert\NotBlank] public string $footer; + + public function getDto(): MessageContentDto + { + return new MessageContentDto( + subject: $this->subject, + text: $this->text, + textMessage: $this->textMessage, + footer: $this->footer, + ); + } } diff --git a/src/Entity/Request/Message/MessageFormatRequest.php b/src/Entity/Request/Message/MessageFormatRequest.php index c46f954..dd95a96 100644 --- a/src/Entity/Request/Message/MessageFormatRequest.php +++ b/src/Entity/Request/Message/MessageFormatRequest.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Entity\Request\Message; +use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageFormatDto; use Symfony\Component\Validator\Constraints as Assert; class MessageFormatRequest implements RequestDtoInterface @@ -19,4 +20,13 @@ class MessageFormatRequest implements RequestDtoInterface new Assert\Choice(['text', 'html', 'pdf']), ])] public array $formatOptions; + + public function getDto(): MessageFormatDto + { + return new MessageFormatDto( + htmlFormated: $this->htmlFormated, + sendFormat: $this->sendFormat, + formatOptions: $this->formatOptions, + ); + } } diff --git a/src/Entity/Request/Message/MessageMetadataRequest.php b/src/Entity/Request/Message/MessageMetadataRequest.php index 9720b66..4e910c2 100644 --- a/src/Entity/Request/Message/MessageMetadataRequest.php +++ b/src/Entity/Request/Message/MessageMetadataRequest.php @@ -4,10 +4,18 @@ namespace PhpList\RestBundle\Entity\Request\Message; +use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageMetadataDto; use Symfony\Component\Validator\Constraints as Assert; class MessageMetadataRequest implements RequestDtoInterface { #[Assert\NotBlank] public string $status; + + public function getDto(): MessageMetadataDto + { + return new MessageMetadataDto( + status: $this->status, + ); + } } diff --git a/src/Entity/Request/Message/MessageOptionsRequest.php b/src/Entity/Request/Message/MessageOptionsRequest.php index cafd7c6..2c67f84 100644 --- a/src/Entity/Request/Message/MessageOptionsRequest.php +++ b/src/Entity/Request/Message/MessageOptionsRequest.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Entity\Request\Message; +use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageOptionsDto; use Symfony\Component\Validator\Constraints as Assert; class MessageOptionsRequest implements RequestDtoInterface @@ -19,4 +20,14 @@ class MessageOptionsRequest implements RequestDtoInterface public ?string $replyTo = null; public ?string $userSelection = null; + + public function getDto(): MessageOptionsDto + { + return new MessageOptionsDto( + fromField: $this->fromField, + toField: $this->toField, + replyTo: $this->replyTo, + userSelection: $this->userSelection, + ); + } } diff --git a/src/Entity/Request/Message/MessageScheduleRequest.php b/src/Entity/Request/Message/MessageScheduleRequest.php index c29c9b8..49226fc 100644 --- a/src/Entity/Request/Message/MessageScheduleRequest.php +++ b/src/Entity/Request/Message/MessageScheduleRequest.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Entity\Request\Message; +use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageScheduleDto; use Symfony\Component\Validator\Constraints as Assert; class MessageScheduleRequest implements RequestDtoInterface @@ -20,4 +21,15 @@ class MessageScheduleRequest implements RequestDtoInterface #[Assert\NotBlank] public string $embargo; + + public function getDto(): MessageScheduleDto + { + return new MessageScheduleDto( + embargo: $this->embargo, + repeatInterval: $this->repeatInterval, + repeatUntil: $this->repeatUntil, + requeueInterval: $this->requeueInterval, + requeueUntil: $this->requeueUntil, + ); + } } diff --git a/src/Entity/Request/RequestInterface.php b/src/Entity/Request/RequestInterface.php index 5c13ace..d87ad38 100644 --- a/src/Entity/Request/RequestInterface.php +++ b/src/Entity/Request/RequestInterface.php @@ -6,5 +6,5 @@ interface RequestInterface { - + public function getDto(): mixed; } diff --git a/src/Entity/Request/SubscriptionRequest.php b/src/Entity/Request/SubscriptionRequest.php index c31e087..16d2239 100644 --- a/src/Entity/Request/SubscriptionRequest.php +++ b/src/Entity/Request/SubscriptionRequest.php @@ -17,4 +17,9 @@ class SubscriptionRequest implements RequestInterface new EmailExists() ])] public array $emails = []; + + public function getDto(): SubscriptionRequest + { + return $this; + } } diff --git a/src/Entity/Request/UpdateAdministratorRequest.php b/src/Entity/Request/UpdateAdministratorRequest.php index 52310ce..2f58fe1 100644 --- a/src/Entity/Request/UpdateAdministratorRequest.php +++ b/src/Entity/Request/UpdateAdministratorRequest.php @@ -5,10 +5,11 @@ namespace PhpList\RestBundle\Entity\Request; use PhpList\Core\Domain\Model\Identity\Administrator; +use PhpList\Core\Domain\Model\Identity\Dto\UpdateAdministratorDto; use Symfony\Component\Validator\Constraints as Assert; use PhpList\RestBundle\Validator\Constraint as CustomAssert; -class UpdateAdministratorRequest +class UpdateAdministratorRequest implements RequestInterface { public int $administratorId; @@ -25,4 +26,15 @@ class UpdateAdministratorRequest #[Assert\Type('bool')] public ?bool $superAdmin = null; + + public function getDto(): UpdateAdministratorDto + { + return new UpdateAdministratorDto( + administratorId: $this->administratorId, + loginName: $this->loginName, + password: $this->password, + email: $this->email, + superAdmin: $this->superAdmin, + ); + } } diff --git a/src/Entity/Request/UpdateMessageRequest.php b/src/Entity/Request/UpdateMessageRequest.php index eb7bd44..6cf43f1 100644 --- a/src/Entity/Request/UpdateMessageRequest.php +++ b/src/Entity/Request/UpdateMessageRequest.php @@ -4,7 +4,23 @@ namespace PhpList\RestBundle\Entity\Request; +use PhpList\Core\Domain\Model\Messaging\Dto\MessageDtoInterface; +use PhpList\Core\Domain\Model\Messaging\Dto\UpdateMessageDto; + class UpdateMessageRequest extends CreateMessageRequest { public int $messageId; + + public function getDto(): MessageDtoInterface + { + return new UpdateMessageDto( + messageId: $this->messageId, + content: $this->content->getDto(), + format: $this->format->getDto(), + metadata: $this->metadata->getDto(), + options: $this->options->getDto(), + schedule: $this->schedule->getDto(), + templateId: $this->templateId, + ); + } } diff --git a/src/Entity/Request/UpdateSubscriberRequest.php b/src/Entity/Request/UpdateSubscriberRequest.php index b74ebd2..803e99e 100644 --- a/src/Entity/Request/UpdateSubscriberRequest.php +++ b/src/Entity/Request/UpdateSubscriberRequest.php @@ -4,6 +4,7 @@ namespace PhpList\RestBundle\Entity\Request; +use PhpList\Core\Domain\Model\Subscription\Dto\UpdateSubscriberDto; use PhpList\Core\Domain\Model\Subscription\Subscriber; use PhpList\RestBundle\Validator\Constraint as CustomAssert; use Symfony\Component\Validator\Constraints as Assert; @@ -31,4 +32,17 @@ class UpdateSubscriberRequest implements RequestInterface #[Assert\Type(type: 'string')] public string $additionalData; + + public function getDto(): UpdateSubscriberDto + { + return new UpdateSubscriberDto( + subscriberId: $this->subscriberId, + email: $this->email, + confirmed: $this->confirmed, + blacklisted: $this->blacklisted, + htmlEmail: $this->htmlEmail, + disabled: $this->disabled, + additionalData: $this->additionalData, + ); + } } diff --git a/src/EventListener/ExceptionListener.php b/src/EventListener/ExceptionListener.php index 1dc4662..bfdb7b8 100644 --- a/src/EventListener/ExceptionListener.php +++ b/src/EventListener/ExceptionListener.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\EventListener; use Exception; -use PhpList\RestBundle\Exception\SubscriptionCreationException; +use PhpList\Core\Domain\Exception\SubscriptionCreationException; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; diff --git a/src/Exception/SubscriptionCreationException.php b/src/Exception/SubscriptionCreationException.php deleted file mode 100644 index 4628421..0000000 --- a/src/Exception/SubscriptionCreationException.php +++ /dev/null @@ -1,23 +0,0 @@ -statusCode = $statusCode; - } - - public function getStatusCode(): int - { - return $this->statusCode; - } -} diff --git a/src/Service/Builder/BuilderFromDtoInterface.php b/src/Service/Builder/BuilderFromDtoInterface.php deleted file mode 100644 index 2778d9a..0000000 --- a/src/Service/Builder/BuilderFromDtoInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -messageFormatBuilder->buildFromDto($request->format); - $schedule = $this->messageScheduleBuilder->buildFromDto($request->schedule); - $content = $this->messageContentBuilder->buildFromDto($request->content); - $options = $this->messageOptionsBuilder->buildFromDto($request->options); - $template = null; - if (isset($request->templateId)) { - $template = $this->templateRepository->find($request->templateId); - } - - if ($context->getExisting()) { - $context->getExisting()->setFormat($format); - $context->getExisting()->setSchedule($schedule); - $context->getExisting()->setContent($content); - $context->getExisting()->setOptions($options); - $context->getExisting()->setTemplate($template); - return $context->getExisting(); - } - - $metadata = new Message\MessageMetadata($request->metadata->status); - - return new Message($format, $schedule, $metadata, $content, $options, $context->getOwner(), $template); - } -} diff --git a/src/Service/Builder/MessageContentBuilder.php b/src/Service/Builder/MessageContentBuilder.php deleted file mode 100644 index e3c8b47..0000000 --- a/src/Service/Builder/MessageContentBuilder.php +++ /dev/null @@ -1,26 +0,0 @@ -subject, - $dto->text, - $dto->textMessage, - $dto->footer - ); - } -} diff --git a/src/Service/Builder/MessageFormatBuilder.php b/src/Service/Builder/MessageFormatBuilder.php deleted file mode 100644 index f5d43b0..0000000 --- a/src/Service/Builder/MessageFormatBuilder.php +++ /dev/null @@ -1,25 +0,0 @@ -htmlFormated, - $dto->sendFormat, - $dto->formatOptions - ); - } -} diff --git a/src/Service/Builder/MessageOptionsBuilder.php b/src/Service/Builder/MessageOptionsBuilder.php deleted file mode 100644 index 8327157..0000000 --- a/src/Service/Builder/MessageOptionsBuilder.php +++ /dev/null @@ -1,27 +0,0 @@ -fromField ?? '', - $dto->toField ?? '', - $dto->replyTo ?? '', - $dto->userSelection, - null, - ); - } -} diff --git a/src/Service/Builder/MessageScheduleBuilder.php b/src/Service/Builder/MessageScheduleBuilder.php deleted file mode 100644 index 95463ca..0000000 --- a/src/Service/Builder/MessageScheduleBuilder.php +++ /dev/null @@ -1,28 +0,0 @@ -repeatInterval, - new DateTime($dto->repeatUntil), - $dto->requeueInterval, - new DateTime($dto->requeueUntil), - new DateTime($dto->embargo) - ); - } -} diff --git a/src/Service/Manager/AdministratorManager.php b/src/Service/Manager/AdministratorManager.php deleted file mode 100644 index a56a809..0000000 --- a/src/Service/Manager/AdministratorManager.php +++ /dev/null @@ -1,63 +0,0 @@ -entityManager = $entityManager; - $this->hashGenerator = $hashGenerator; - } - - public function createAdministrator(CreateAdministratorRequest $dto): Administrator - { - $administrator = new Administrator(); - $administrator->setLoginName($dto->loginName); - $administrator->setEmail($dto->email); - $administrator->setSuperUser($dto->superUser); - $hashedPassword = $this->hashGenerator->createPasswordHash($dto->password); - $administrator->setPasswordHash($hashedPassword); - - $this->entityManager->persist($administrator); - $this->entityManager->flush(); - - return $administrator; - } - - public function updateAdministrator(Administrator $administrator, UpdateAdministratorRequest $dto): void - { - if ($dto->loginName !== null) { - $administrator->setLoginName($dto->loginName); - } - if ($dto->email !== null) { - $administrator->setEmail($dto->email); - } - if ($dto->superAdmin !== null) { - $administrator->setSuperUser($dto->superAdmin); - } - if ($dto->password !== null) { - $hashedPassword = $this->hashGenerator->createPasswordHash($dto->password); - $administrator->setPasswordHash($hashedPassword); - } - - $this->entityManager->flush(); - } - - public function deleteAdministrator(Administrator $administrator): void - { - $this->entityManager->remove($administrator); - $this->entityManager->flush(); - } -} diff --git a/src/Service/Manager/MessageManager.php b/src/Service/Manager/MessageManager.php deleted file mode 100644 index 96dc11d..0000000 --- a/src/Service/Manager/MessageManager.php +++ /dev/null @@ -1,57 +0,0 @@ -messageRepository = $messageRepository; - $this->messageBuilder = $messageBuilder; - } - - public function createMessage(CreateMessageRequest $createMessageRequest, Administrator $authUser): Message - { - $context = new MessageContext($authUser); - $message = $this->messageBuilder->buildFromRequest($createMessageRequest, $context); - $this->messageRepository->save($message); - - return $message; - } - - public function updateMessage( - UpdateMessageRequest $updateMessageRequest, - Message $message, - Administrator $authUser - ): Message { - $context = new MessageContext($authUser, $message); - $message = $this->messageBuilder->buildFromRequest($updateMessageRequest, $context); - $this->messageRepository->save($message); - - return $message; - } - - public function delete(Message $message): void - { - $this->messageRepository->remove($message); - } - - /** @return Message[] */ - public function getMessagesByOwner(Administrator $owner): array - { - return $this->messageRepository->getByOwnerId($owner->getId()); - } -} diff --git a/src/Service/Manager/SessionManager.php b/src/Service/Manager/SessionManager.php deleted file mode 100644 index f0d02cc..0000000 --- a/src/Service/Manager/SessionManager.php +++ /dev/null @@ -1,49 +0,0 @@ -tokenRepository = $tokenRepository; - $this->administratorRepository = $administratorRepository; - } - - public function createSession(CreateSessionRequest $createSessionRequest): AdministratorToken - { - $administrator = $this->administratorRepository->findOneByLoginCredentials( - $createSessionRequest->loginName, - $createSessionRequest->password - ); - if ($administrator === null) { - throw new UnauthorizedHttpException('', 'Not authorized', null, 1500567098); - } - - $token = new AdministratorToken(); - $token->setAdministrator($administrator); - $token->generateExpiry(); - $token->generateKey(); - $this->tokenRepository->save($token); - - return $token; - } - - public function deleteSession(AdministratorToken $token): void - { - $this->tokenRepository->remove($token); - } -} diff --git a/src/Service/Manager/SubscriberListManager.php b/src/Service/Manager/SubscriberListManager.php deleted file mode 100644 index 7e8346e..0000000 --- a/src/Service/Manager/SubscriberListManager.php +++ /dev/null @@ -1,55 +0,0 @@ -subscriberListRepository = $subscriberListRepository; - } - - public function createSubscriberList( - CreateSubscriberListRequest $subscriberListRequest, - Administrator $authUser - ): SubscriberList { - $subscriberList = (new SubscriberList()) - ->setName($subscriberListRequest->name) - ->setOwner($authUser) - ->setDescription($subscriberListRequest->description) - ->setListPosition($subscriberListRequest->listPosition) - ->setPublic($subscriberListRequest->public); - - $this->subscriberListRepository->save($subscriberList); - - return $subscriberList; - } - - /** - * @return SubscriberList[] - */ - public function getPaginated(PaginationCursorRequest $pagination): array - { - return $this->subscriberListRepository->getAfterId($pagination->afterId, $pagination->limit); - } - - public function getTotalCount(): int - { - return $this->subscriberListRepository->count(); - } - - public function delete(SubscriberList $subscriberList): void - { - $this->subscriberListRepository->remove($subscriberList); - } -} diff --git a/src/Service/Manager/SubscriberManager.php b/src/Service/Manager/SubscriberManager.php deleted file mode 100644 index 174dcdc..0000000 --- a/src/Service/Manager/SubscriberManager.php +++ /dev/null @@ -1,72 +0,0 @@ -subscriberRepository = $subscriberRepository; - $this->entityManager = $entityManager; - } - - public function createSubscriber(CreateSubscriberRequest $subscriberRequest): Subscriber - { - $subscriber = new Subscriber(); - $subscriber->setEmail($subscriberRequest->email); - $confirmed = (bool)$subscriberRequest->requestConfirmation; - $subscriber->setConfirmed(!$confirmed); - $subscriber->setBlacklisted(false); - $subscriber->setHtmlEmail((bool)$subscriberRequest->htmlEmail); - $subscriber->setDisabled(false); - - $this->subscriberRepository->save($subscriber); - - return $subscriber; - } - - public function getSubscriber(int $subscriberId): Subscriber - { - $subscriber = $this->subscriberRepository->findSubscriberWithSubscriptions($subscriberId); - - if (!$subscriber) { - throw new NotFoundHttpException('Subscriber not found'); - } - - return $subscriber; - } - - public function updateSubscriber(UpdateSubscriberRequest $subscriberRequest): Subscriber - { - /** @var Subscriber $subscriber */ - $subscriber = $this->subscriberRepository->find($subscriberRequest->subscriberId); - - $subscriber->setEmail($subscriberRequest->email); - $subscriber->setConfirmed($subscriberRequest->confirmed); - $subscriber->setBlacklisted($subscriberRequest->blacklisted); - $subscriber->setHtmlEmail($subscriberRequest->htmlEmail); - $subscriber->setDisabled($subscriberRequest->disabled); - $subscriber->setExtraData($subscriberRequest->additionalData); - - $this->entityManager->flush(); - - return $subscriber; - } - - public function deleteSubscriber(Subscriber $subscriber): void - { - $this->subscriberRepository->remove($subscriber); - } -} diff --git a/src/Service/Manager/SubscriptionManager.php b/src/Service/Manager/SubscriptionManager.php deleted file mode 100644 index ab410ea..0000000 --- a/src/Service/Manager/SubscriptionManager.php +++ /dev/null @@ -1,90 +0,0 @@ -subscriptionRepository = $subscriptionRepository; - $this->subscriberRepository = $subscriberRepository; - } - - /** @return Subscription[] */ - public function createSubscriptions(SubscriberList $subscriberList, array $emails): array - { - $subscriptions = []; - foreach ($emails as $email) { - $subscriptions[] = $this->createSubscription($subscriberList, $email); - } - - return $subscriptions; - } - - private function createSubscription(SubscriberList $subscriberList, string $email): Subscription - { - $subscriber = $this->subscriberRepository->findOneBy(['email' => $email]); - if (!$subscriber) { - throw new SubscriptionCreationException('Subscriber does not exists.', 404); - } - - $existingSubscription = $this->subscriptionRepository - ->findOneBySubscriberListAndSubscriber($subscriberList, $subscriber); - if ($existingSubscription) { - return $existingSubscription; - } - - $subscription = new Subscription(); - $subscription->setSubscriber($subscriber); - $subscription->setSubscriberList($subscriberList); - - $this->subscriptionRepository->save($subscription); - - return $subscription; - } - - public function deleteSubscriptions(SubscriberList $subscriberList, array $emails): void - { - foreach ($emails as $email) { - try { - $this->deleteSubscription($subscriberList, $email); - } catch (SubscriptionCreationException $e) { - if ($e->getStatusCode() !== 404) { - throw $e; - } - } - } - } - - private function deleteSubscription(SubscriberList $subscriberList, string $email): void - { - $subscription = $this->subscriptionRepository - ->findOneBySubscriberEmailAndListId($subscriberList->getId(), $email); - - if (!$subscription) { - throw new SubscriptionCreationException('Subscription not found for this subscriber and list.', 404); - } - - $this->subscriptionRepository->remove($subscription); - } - - /** @return Subscriber[] */ - public function getSubscriberListMembers(SubscriberList $list): array - { - return $this->subscriberRepository->getSubscribersBySubscribedListId($list->getId()); - } -} diff --git a/src/Service/Manager/TemplateImageManager.php b/src/Service/Manager/TemplateImageManager.php deleted file mode 100644 index 3a594c2..0000000 --- a/src/Service/Manager/TemplateImageManager.php +++ /dev/null @@ -1,104 +0,0 @@ - 'image/gif', - 'jpg' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'bmp' => 'image/bmp', - 'png' => 'image/png', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff', - 'swf' => 'application/x-shockwave-flash', - ]; - - private TemplateImageRepository $templateImageRepository; - private EntityManagerInterface $entityManager; - - public function __construct( - TemplateImageRepository $templateImageRepository, - EntityManagerInterface $entityManager - ) { - $this->templateImageRepository = $templateImageRepository; - $this->entityManager = $entityManager; - } - - /** @return TemplateImage[] */ - public function createImagesFromImagePaths(array $imagePaths, Template $template): array - { - $templateImages = []; - foreach ($imagePaths as $path) { - $image = new TemplateImage(); - $image->setTemplate($template); - $image->setFilename($path); - $image->setMimeType($this->guessMimeType($path)); - $image->setData(null); - - $this->entityManager->persist($image); - $templateImages[] = $image; - } - - $this->entityManager->flush(); - - return $templateImages; - } - - private function guessMimeType(string $filename): string - { - $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); - return self::IMAGE_MIME_TYPES[$ext] ?? 'application/octet-stream'; - } - - public function extractAllImages(string $html): array - { - $fromRegex = array_keys( - $this->extractTemplateImagesFromContent($html) - ); - - $fromDom = $this->extractImagesFromHtml($html); - - return array_values(array_unique(array_merge($fromRegex, $fromDom))); - } - - private function extractTemplateImagesFromContent(string $content): array - { - $regexp = sprintf('/"([^"]+\.(%s))"/Ui', implode('|', array_keys(self::IMAGE_MIME_TYPES))); - preg_match_all($regexp, stripslashes($content), $images); - - return array_count_values($images[1]); - } - - private function extractImagesFromHtml(string $html): array - { - $dom = new DOMDocument(); - // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - @$dom->loadHTML($html); - $images = []; - - foreach ($dom->getElementsByTagName('img') as $img) { - $src = $img->getAttribute('src'); - if ($src) { - $images[] = $src; - } - } - - return $images; - } - - public function delete(TemplateImage $templateImage): void - { - $this->templateImageRepository->remove($templateImage); - } -} diff --git a/src/Service/Manager/TemplateManager.php b/src/Service/Manager/TemplateManager.php deleted file mode 100644 index 5e85e12..0000000 --- a/src/Service/Manager/TemplateManager.php +++ /dev/null @@ -1,88 +0,0 @@ -templateRepository = $templateRepository; - $this->entityManager = $entityManager; - $this->templateImageManager = $templateImageManager; - $this->templateLinkValidator = $templateLinkValidator; - $this->templateImageValidator = $templateImageValidator; - } - - public function create(CreateTemplateRequest $request): Template - { - $template = (new Template($request->title)) - ->setContent($request->content) - ->setText($request->text); - - if ($request->file instanceof UploadedFile) { - $template->setContent(file_get_contents($request->file->getPathname())); - } - - $context = (new ValidationContext()) - ->set('checkLinks', $request->checkLinks) - ->set('checkImages', $request->checkImages) - ->set('checkExternalImages', $request->checkExternalImages); - - $this->templateLinkValidator->validate($template->getContent() ?? '', $context); - - $imageUrls = $this->templateImageManager->extractAllImages($template->getContent() ?? ''); - $this->templateImageValidator->validate($imageUrls, $context); - - $this->templateRepository->save($template); - - $this->templateImageManager->createImagesFromImagePaths($imageUrls, $template); - - return $template; - } - - public function update(UpdateSubscriberRequest $subscriberRequest): Subscriber - { - /** @var Subscriber $subscriber */ - $subscriber = $this->templateRepository->find($subscriberRequest->subscriberId); - - $subscriber->setEmail($subscriberRequest->email); - $subscriber->setConfirmed($subscriberRequest->confirmed); - $subscriber->setBlacklisted($subscriberRequest->blacklisted); - $subscriber->setHtmlEmail($subscriberRequest->htmlEmail); - $subscriber->setDisabled($subscriberRequest->disabled); - $subscriber->setExtraData($subscriberRequest->additionalData); - - $this->entityManager->flush(); - - return $subscriber; - } - - public function delete(Template $template): void - { - $this->templateRepository->remove($template); - } -} diff --git a/src/Service/Provider/PaginatedDataProvider.php b/src/Service/Provider/PaginatedDataProvider.php index b26657a..1c8fcef 100644 --- a/src/Service/Provider/PaginatedDataProvider.php +++ b/src/Service/Provider/PaginatedDataProvider.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Service\Provider; use Doctrine\ORM\EntityManagerInterface; -use PhpList\Core\Domain\Filter\FilterRequestInterface; +use PhpList\Core\Domain\Model\Dto\Filter\FilterRequestInterface; use PhpList\Core\Domain\Repository\Interfaces\PaginatableRepositoryInterface; use PhpList\RestBundle\Entity\Dto\CursorPaginationResult; use PhpList\RestBundle\Serializer\CursorPaginationNormalizer; diff --git a/src/Validator/TemplateImageValidator.php b/src/Validator/TemplateImageValidator.php deleted file mode 100644 index b89da9a..0000000 --- a/src/Validator/TemplateImageValidator.php +++ /dev/null @@ -1,72 +0,0 @@ -get('checkImages', false); - $checkExist = $context?->get('checkExternalImages', false); - - $errors = array_merge( - $checkFull ? $this->validateFullUrls($value) : [], - $checkExist ? $this->validateExistence($value) : [] - ); - - if (!empty($errors)) { - throw new ValidatorException(implode("\n", $errors)); - } - } - - private function validateFullUrls(array $urls): array - { - $errors = []; - - foreach ($urls as $url) { - if (!preg_match('#^https?://#i', $url)) { - $errors[] = sprintf('Image "%s" is not a full URL.', $url); - } - } - - return $errors; - } - - private function validateExistence(array $urls): array - { - $errors = []; - - foreach ($urls as $url) { - if (!preg_match('#^https?://#i', $url)) { - continue; - } - - try { - $response = $this->httpClient->request('HEAD', $url); - if ($response->getStatusCode() !== 200) { - $errors[] = sprintf('Image "%s" does not exist (HTTP %s)', $url, $response->getStatusCode()); - } - } catch (Throwable $e) { - $errors[] = sprintf('Image "%s" could not be validated: %s', $url, $e->getMessage()); - } - } - - return $errors; - } -} diff --git a/src/Validator/TemplateLinkValidator.php b/src/Validator/TemplateLinkValidator.php deleted file mode 100644 index 78916bc..0000000 --- a/src/Validator/TemplateLinkValidator.php +++ /dev/null @@ -1,62 +0,0 @@ -get('checkLinks', false)) { - return; - } - $links = $this->extractLinks($value); - $invalid = []; - - foreach ($links as $link) { - if (!preg_match('#^https?://#i', $link) && - !preg_match('#^mailto:#i', $link) && - !in_array(strtoupper($link), self::PLACEHOLDERS, true) - ) { - $invalid[] = $link; - } - } - - if (!empty($invalid)) { - throw new ValidatorException(sprintf( - 'Not full URLs: %s', - implode(', ', $invalid) - )); - } - } - - private function extractLinks(string $html): array - { - $dom = new DOMDocument(); - // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged - @$dom->loadHTML($html); - $links = []; - - foreach ($dom->getElementsByTagName('a') as $node) { - $href = $node->getAttribute('href'); - if ($href) { - $links[] = $href; - } - } - - return $links; - } -} diff --git a/src/Validator/ValidatorInterface.php b/src/Validator/ValidatorInterface.php deleted file mode 100644 index 9b496fa..0000000 --- a/src/Validator/ValidatorInterface.php +++ /dev/null @@ -1,12 +0,0 @@ -createMock(HttpKernelInterface::class); $request = new Request(); diff --git a/tests/Unit/Service/Builder/MessageBuilderTest.php b/tests/Unit/Service/Builder/MessageBuilderTest.php deleted file mode 100644 index ba2ead4..0000000 --- a/tests/Unit/Service/Builder/MessageBuilderTest.php +++ /dev/null @@ -1,148 +0,0 @@ -createMock(TemplateRepository::class); - $this->formatBuilder = $this->createMock(MessageFormatBuilder::class); - $this->scheduleBuilder = $this->createMock(MessageScheduleBuilder::class); - $this->contentBuilder = $this->createMock(MessageContentBuilder::class); - $this->optionsBuilder = $this->createMock(MessageOptionsBuilder::class); - - $this->builder = new MessageBuilder( - $templateRepository, - $this->formatBuilder, - $this->scheduleBuilder, - $this->contentBuilder, - $this->optionsBuilder - ); - } - - private function createRequest(): CreateMessageRequest - { - $request = new CreateMessageRequest(); - $request->format = new MessageFormatRequest(); - $request->schedule = new MessageScheduleRequest(); - $request->content = new MessageContentRequest(); - $request->metadata = new MessageMetadataRequest(); - $request->metadata->status = 'draft'; - $request->options = new MessageOptionsRequest(); - $request->templateId = 0; - - return $request; - } - - private function mockBuildFromDtoCalls(CreateMessageRequest $request): void - { - $this->formatBuilder->expects($this->once()) - ->method('buildFromDto') - ->with($request->format) - ->willReturn($this->createMock(Message\MessageFormat::class)); - - $this->scheduleBuilder->expects($this->once()) - ->method('buildFromDto') - ->with($request->schedule) - ->willReturn($this->createMock(Message\MessageSchedule::class)); - - $this->contentBuilder->expects($this->once()) - ->method('buildFromDto') - ->with($request->content) - ->willReturn($this->createMock(Message\MessageContent::class)); - - $this->optionsBuilder->expects($this->once()) - ->method('buildFromDto') - ->with($request->options) - ->willReturn($this->createMock(Message\MessageOptions::class)); - } - - public function testBuildsNewMessage(): void - { - $request = $this->createRequest(); - $admin = $this->createMock(Administrator::class); - $context = new MessageContext($admin); - - $this->mockBuildFromDtoCalls($request); - - $this->builder->buildFromRequest($request, $context); - } - - public function testThrowsExceptionOnInvalidRequest(): void - { - $this->expectException(InvalidArgumentException::class); - - $this->builder->buildFromRequest( - $this->createMock(RequestInterface::class), - new MessageContext($this->createMock(Administrator::class)) - ); - } - - public function testThrowsExceptionOnInvalidContext(): void - { - $this->expectException(InvalidArgumentException::class); - - $this->builder->buildFromRequest(new CreateMessageRequest(), new \stdClass()); - } - - public function testUpdatesExistingMessage(): void - { - $request = $this->createRequest(); - $admin = $this->createMock(Administrator::class); - $existingMessage = $this->createMock(Message::class); - $context = new MessageContext($admin, $existingMessage); - - $this->mockBuildFromDtoCalls($request); - - $existingMessage - ->expects($this->once()) - ->method('setFormat') - ->with($this->isInstanceOf(Message\MessageFormat::class)); - $existingMessage - ->expects($this->once()) - ->method('setSchedule') - ->with($this->isInstanceOf(Message\MessageSchedule::class)); - $existingMessage - ->expects($this->once()) - ->method('setContent') - ->with($this->isInstanceOf(Message\MessageContent::class)); - $existingMessage - ->expects($this->once()) - ->method('setOptions') - ->with($this->isInstanceOf(Message\MessageOptions::class)); - $existingMessage->expects($this->once())->method('setTemplate')->with(null); - - $result = $this->builder->buildFromRequest($request, $context); - - $this->assertSame($existingMessage, $result); - } -} diff --git a/tests/Unit/Service/Builder/MessageContentBuilderTest.php b/tests/Unit/Service/Builder/MessageContentBuilderTest.php deleted file mode 100644 index 7a45d4c..0000000 --- a/tests/Unit/Service/Builder/MessageContentBuilderTest.php +++ /dev/null @@ -1,44 +0,0 @@ -builder = new MessageContentBuilder(); - } - - public function testBuildsMessageContentSuccessfully(): void - { - $dto = new MessageContentRequest(); - $dto->subject = 'Test Subject'; - $dto->text = 'Full text content'; - $dto->textMessage = 'Short text version'; - $dto->footer = 'Footer text'; - - $messageContent = $this->builder->buildFromDto($dto); - - $this->assertSame('Test Subject', $messageContent->getSubject()); - $this->assertSame('Full text content', $messageContent->getText()); - $this->assertSame('Short text version', $messageContent->getTextMessage()); - $this->assertSame('Footer text', $messageContent->getFooter()); - } - - public function testThrowsExceptionOnInvalidDto(): void - { - $this->expectException(InvalidArgumentException::class); - - $invalidDto = new \stdClass(); - $this->builder->buildFromDto($invalidDto); - } -} diff --git a/tests/Unit/Service/Builder/MessageFormatBuilderTest.php b/tests/Unit/Service/Builder/MessageFormatBuilderTest.php deleted file mode 100644 index a561248..0000000 --- a/tests/Unit/Service/Builder/MessageFormatBuilderTest.php +++ /dev/null @@ -1,42 +0,0 @@ -builder = new MessageFormatBuilder(); - } - - public function testBuildsMessageFormatSuccessfully(): void - { - $dto = new MessageFormatRequest(); - $dto->htmlFormated = true; - $dto->sendFormat = 'html'; - $dto->formatOptions = ['html', 'text']; - - $messageFormat = $this->builder->buildFromDto($dto); - - $this->assertSame(true, $messageFormat->isHtmlFormatted()); - $this->assertSame('html', $messageFormat->getSendFormat()); - $this->assertEqualsCanonicalizing(['html', 'text'], $messageFormat->getFormatOptions()); - } - - public function testThrowsExceptionOnInvalidDto(): void - { - $this->expectException(InvalidArgumentException::class); - - $invalidDto = new \stdClass(); - $this->builder->buildFromDto($invalidDto); - } -} diff --git a/tests/Unit/Service/Builder/MessageOptionsBuilderTest.php b/tests/Unit/Service/Builder/MessageOptionsBuilderTest.php deleted file mode 100644 index 30fe00c..0000000 --- a/tests/Unit/Service/Builder/MessageOptionsBuilderTest.php +++ /dev/null @@ -1,44 +0,0 @@ -builder = new MessageOptionsBuilder(); - } - - public function testBuildsMessageOptionsSuccessfully(): void - { - $dto = new MessageOptionsRequest(); - $dto->fromField = 'info@example.com'; - $dto->toField = 'user@example.com'; - $dto->replyTo = 'reply@example.com'; - $dto->userSelection = 'all-users'; - - $messageOptions = $this->builder->buildFromDto($dto); - - $this->assertSame('info@example.com', $messageOptions->getFromField()); - $this->assertSame('user@example.com', $messageOptions->getToField()); - $this->assertSame('reply@example.com', $messageOptions->getReplyTo()); - $this->assertSame('all-users', $messageOptions->getUserSelection()); - } - - public function testThrowsExceptionOnInvalidDto(): void - { - $this->expectException(InvalidArgumentException::class); - - $invalidDto = new \stdClass(); - $this->builder->buildFromDto($invalidDto); - } -} diff --git a/tests/Unit/Service/Builder/MessageScheduleBuilderTest.php b/tests/Unit/Service/Builder/MessageScheduleBuilderTest.php deleted file mode 100644 index 85078c4..0000000 --- a/tests/Unit/Service/Builder/MessageScheduleBuilderTest.php +++ /dev/null @@ -1,47 +0,0 @@ -builder = new MessageScheduleBuilder(); - } - - public function testBuildsMessageScheduleSuccessfully(): void - { - $dto = new MessageScheduleRequest(); - $dto->repeatInterval = 1440; - $dto->repeatUntil = '2025-04-30T00:00:00+00:00'; - $dto->requeueInterval = 720; - $dto->requeueUntil = '2025-04-20T00:00:00+00:00'; - $dto->embargo = '2025-04-17T09:00:00+00:00'; - - $messageSchedule = $this->builder->buildFromDto($dto); - - $this->assertSame(1440, $messageSchedule->getRepeatInterval()); - $this->assertEquals(new DateTime('2025-04-30T00:00:00+00:00'), $messageSchedule->getRepeatUntil()); - $this->assertSame(720, $messageSchedule->getRequeueInterval()); - $this->assertEquals(new DateTime('2025-04-20T00:00:00+00:00'), $messageSchedule->getRequeueUntil()); - $this->assertEquals(new DateTime('2025-04-17T09:00:00+00:00'), $messageSchedule->getEmbargo()); - } - - public function testThrowsExceptionOnInvalidDto(): void - { - $this->expectException(InvalidArgumentException::class); - - $invalidDto = new \stdClass(); - $this->builder->buildFromDto($invalidDto); - } -} diff --git a/tests/Unit/Service/Manager/AdministratorManagerTest.php b/tests/Unit/Service/Manager/AdministratorManagerTest.php deleted file mode 100644 index 39f441b..0000000 --- a/tests/Unit/Service/Manager/AdministratorManagerTest.php +++ /dev/null @@ -1,94 +0,0 @@ -createMock(EntityManagerInterface::class); - $hashGenerator = $this->createMock(HashGenerator::class); - - $dto = new CreateAdministratorRequest(); - $dto->loginName = 'admin'; - $dto->email = 'admin@example.com'; - $dto->superUser = true; - $dto->password = 'securepass'; - - $hashGenerator->expects($this->once()) - ->method('createPasswordHash') - ->with('securepass') - ->willReturn('hashed_pass'); - - $entityManager->expects($this->once())->method('persist'); - $entityManager->expects($this->once())->method('flush'); - - $manager = new AdministratorManager($entityManager, $hashGenerator); - $admin = $manager->createAdministrator($dto); - - $this->assertInstanceOf(Administrator::class, $admin); - $this->assertEquals('admin', $admin->getLoginName()); - $this->assertEquals('admin@example.com', $admin->getEmail()); - $this->assertEquals(true, $admin->isSuperUser()); - $this->assertEquals('hashed_pass', $admin->getPasswordHash()); - } - - public function testUpdateAdministrator(): void - { - $entityManager = $this->createMock(EntityManagerInterface::class); - $hashGenerator = $this->createMock(HashGenerator::class); - - $admin = new Administrator(); - $admin->setLoginName('old'); - $admin->setEmail('old@example.com'); - $admin->setSuperUser(false); - $admin->setPasswordHash('old_hash'); - - $dto = new UpdateAdministratorRequest(); - $dto->loginName = 'new'; - $dto->email = 'new@example.com'; - $dto->superAdmin = true; - $dto->password = 'newpass'; - - $hashGenerator->expects($this->once()) - ->method('createPasswordHash') - ->with('newpass') - ->willReturn('new_hash'); - - $entityManager->expects($this->once())->method('flush'); - - $manager = new AdministratorManager($entityManager, $hashGenerator); - $manager->updateAdministrator($admin, $dto); - - $this->assertEquals('new', $admin->getLoginName()); - $this->assertEquals('new@example.com', $admin->getEmail()); - $this->assertTrue($admin->isSuperUser()); - $this->assertEquals('new_hash', $admin->getPasswordHash()); - } - - public function testDeleteAdministrator(): void - { - $entityManager = $this->createMock(EntityManagerInterface::class); - $hashGenerator = $this->createMock(HashGenerator::class); - - $admin = $this->createMock(Administrator::class); - - $entityManager->expects($this->once())->method('remove')->with($admin); - $entityManager->expects($this->once())->method('flush'); - - $manager = new AdministratorManager($entityManager, $hashGenerator); - $manager->deleteAdministrator($admin); - - $this->assertTrue(true); - } -} diff --git a/tests/Unit/Service/Manager/MessageManagerTest.php b/tests/Unit/Service/Manager/MessageManagerTest.php deleted file mode 100644 index 6b08731..0000000 --- a/tests/Unit/Service/Manager/MessageManagerTest.php +++ /dev/null @@ -1,152 +0,0 @@ -createMock(MessageRepository::class); - $messageBuilder = $this->createMock(MessageBuilder::class); - - $manager = new MessageManager($messageRepository, $messageBuilder); - - $format = new MessageFormatRequest(); - $format->htmlFormated = true; - $format->sendFormat = 'html'; - $format->formatOptions = ['html']; - - $schedule = new MessageScheduleRequest(); - $schedule->repeatInterval = 60 * 24; - $schedule->repeatUntil = '2025-04-30T00:00:00+00:00'; - $schedule->requeueInterval = 60 * 12; - $schedule->requeueUntil = '2025-04-20T00:00:00+00:00'; - $schedule->embargo = '2025-04-17T09:00:00+00:00'; - - $metadata = new MessageMetadataRequest(); - $metadata->status = 'draft'; - - $content = new MessageContentRequest(); - $content->subject = 'Subject'; - $content->text = 'Full text'; - $content->textMessage = 'Short text'; - $content->footer = 'Footer'; - - $options = new MessageOptionsRequest(); - $options->fromField = 'from@example.com'; - $options->toField = 'to@example.com'; - $options->replyTo = 'reply@example.com'; - $options->userSelection = 'all-users'; - - $request = new CreateMessageRequest(); - $request->format = $format; - $request->schedule = $schedule; - $request->metadata = $metadata; - $request->content = $content; - $request->options = $options; - $request->templateId = 0; - - $authUser = $this->createMock(Administrator::class); - - $expectedMessage = $this->createMock(Message::class); - $expectedContent = $this->createMock(Message\MessageContent::class); - $expectedMetadata = $this->createMock(Message\MessageMetadata::class); - - $expectedContent->method('getSubject')->willReturn('Subject'); - $expectedMetadata->method('getStatus')->willReturn('draft'); - - $expectedMessage->method('getContent')->willReturn($expectedContent); - $expectedMessage->method('getMetadata')->willReturn($expectedMetadata); - - $messageBuilder->expects($this->once()) - ->method('buildFromRequest') - ->with($request, $this->anything()) - ->willReturn($expectedMessage); - - $messageRepository->expects($this->once()) - ->method('save') - ->with($expectedMessage); - - $message = $manager->createMessage($request, $authUser); - - $this->assertSame('Subject', $message->getContent()->getSubject()); - $this->assertSame('draft', $message->getMetadata()->getStatus()); - } - - public function testUpdateMessageReturnsUpdatedMessage(): void - { - $messageRepository = $this->createMock(MessageRepository::class); - $messageBuilder = $this->createMock(MessageBuilder::class); - - $manager = new MessageManager($messageRepository, $messageBuilder); - - $updateRequest = new \PhpList\RestBundle\Entity\Request\UpdateMessageRequest(); - $updateRequest->messageId = 1; - $updateRequest->format = new MessageFormatRequest(); - $updateRequest->format->htmlFormated = false; - $updateRequest->format->sendFormat = 'text'; - $updateRequest->format->formatOptions = ['text']; - - $updateRequest->schedule = new MessageScheduleRequest(); - $updateRequest->schedule->repeatInterval = 0; - $updateRequest->schedule->repeatUntil = '2025-04-30T00:00:00+00:00'; - $updateRequest->schedule->requeueInterval = 0; - $updateRequest->schedule->requeueUntil = '2025-04-20T00:00:00+00:00'; - $updateRequest->schedule->embargo = '2025-04-17T09:00:00+00:00'; - - $updateRequest->content = new MessageContentRequest(); - $updateRequest->content->subject = 'Updated Subject'; - $updateRequest->content->text = 'Updated Full text'; - $updateRequest->content->textMessage = 'Updated Short text'; - $updateRequest->content->footer = 'Updated Footer'; - - $updateRequest->options = new MessageOptionsRequest(); - $updateRequest->options->fromField = 'newfrom@example.com'; - $updateRequest->options->toField = 'newto@example.com'; - $updateRequest->options->replyTo = 'newreply@example.com'; - $updateRequest->options->userSelection = 'active-users'; - - $updateRequest->templateId = 2; - - $authUser = $this->createMock(Administrator::class); - - $existingMessage = $this->createMock(Message::class); - $expectedContent = $this->createMock(Message\MessageContent::class); - $expectedMetadata = $this->createMock(Message\MessageMetadata::class); - - $expectedContent->method('getSubject')->willReturn('Updated Subject'); - $expectedMetadata->method('getStatus')->willReturn('draft'); - - $existingMessage->method('getContent')->willReturn($expectedContent); - $existingMessage->method('getMetadata')->willReturn($expectedMetadata); - - $messageBuilder->expects($this->once()) - ->method('buildFromRequest') - ->with($updateRequest, $this->anything()) - ->willReturn($existingMessage); - - $messageRepository->expects($this->once()) - ->method('save') - ->with($existingMessage); - - $message = $manager->updateMessage($updateRequest, $existingMessage, $authUser); - - $this->assertSame('Updated Subject', $message->getContent()->getSubject()); - $this->assertSame('draft', $message->getMetadata()->getStatus()); - } -} diff --git a/tests/Unit/Service/Manager/SessionManagerTest.php b/tests/Unit/Service/Manager/SessionManagerTest.php deleted file mode 100644 index 9489c24..0000000 --- a/tests/Unit/Service/Manager/SessionManagerTest.php +++ /dev/null @@ -1,54 +0,0 @@ -loginName = 'admin'; - $request->password = 'wrong'; - - $adminRepo = $this->createMock(AdministratorRepository::class); - $adminRepo->expects(self::once()) - ->method('findOneByLoginCredentials') - ->with('admin', 'wrong') - ->willReturn(null); - - $tokenRepo = $this->createMock(AdministratorTokenRepository::class); - $tokenRepo->expects(self::never())->method('save'); - - $manager = new SessionManager($tokenRepo, $adminRepo); - - $this->expectException(UnauthorizedHttpException::class); - $this->expectExceptionMessage('Not authorized'); - - $manager->createSession($request); - } - - public function testDeleteSessionCallsRemove(): void - { - $token = $this->createMock(AdministratorToken::class); - - $tokenRepo = $this->createMock(AdministratorTokenRepository::class); - $tokenRepo->expects(self::once()) - ->method('remove') - ->with($token); - - $adminRepo = $this->createMock(AdministratorRepository::class); - - $manager = new SessionManager($tokenRepo, $adminRepo); - $manager->deleteSession($token); - } -} diff --git a/tests/Unit/Service/Manager/SubscriberListManagerTest.php b/tests/Unit/Service/Manager/SubscriberListManagerTest.php deleted file mode 100644 index 20e2d77..0000000 --- a/tests/Unit/Service/Manager/SubscriberListManagerTest.php +++ /dev/null @@ -1,77 +0,0 @@ -subscriberListRepository = $this->createMock(SubscriberListRepository::class); - $this->manager = new SubscriberListManager($this->subscriberListRepository); - } - - public function testCreateSubscriberList(): void - { - $request = new CreateSubscriberListRequest(); - $request->name = 'New List'; - $request->description = 'Description'; - $request->listPosition = 3; - $request->public = true; - - $admin = new Administrator(); - - $this->subscriberListRepository - ->expects($this->once()) - ->method('save') - ->with($this->isInstanceOf(SubscriberList::class)); - - $result = $this->manager->createSubscriberList($request, $admin); - - $this->assertSame('New List', $result->getName()); - $this->assertSame('Description', $result->getDescription()); - $this->assertSame(3, $result->getListPosition()); - $this->assertTrue($result->isPublic()); - $this->assertSame($admin, $result->getOwner()); - } - - public function testGetPaginated(): void - { - $list = new SubscriberList(); - $this->subscriberListRepository - ->expects($this->once()) - ->method('getAfterId') - ->willReturn([$list]); - - $result = $this->manager->getPaginated(new PaginationCursorRequest(0)); - - $this->assertIsArray($result); - $this->assertCount(1, $result); - $this->assertSame($list, $result[0]); - } - - public function testDeleteSubscriberList(): void - { - $subscriberList = new SubscriberList(); - - $this->subscriberListRepository - ->expects($this->once()) - ->method('remove') - ->with($subscriberList); - - $this->manager->delete($subscriberList); - } -} diff --git a/tests/Unit/Service/Manager/SubscriberManagerTest.php b/tests/Unit/Service/Manager/SubscriberManagerTest.php deleted file mode 100644 index a159d91..0000000 --- a/tests/Unit/Service/Manager/SubscriberManagerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -createMock(SubscriberRepository::class); - $emMock = $this->createMock(EntityManagerInterface::class); - $repoMock - ->expects($this->once()) - ->method('save') - ->with($this->callback(function (Subscriber $sub): bool { - return $sub->getEmail() === 'foo@bar.com' - && $sub->isConfirmed() === false - && $sub->isBlacklisted() === false - && $sub->hasHtmlEmail() === true - && $sub->isDisabled() === false; - })); - - $manager = new SubscriberManager($repoMock, $emMock); - - $dto = new CreateSubscriberRequest(); - $dto->email = 'foo@bar.com'; - $dto->requestConfirmation = true; - $dto->htmlEmail = true; - - $result = $manager->createSubscriber($dto); - - $this->assertInstanceOf(Subscriber::class, $result); - $this->assertSame('foo@bar.com', $result->getEmail()); - $this->assertFalse($result->isConfirmed()); - $this->assertFalse($result->isBlacklisted()); - $this->assertTrue($result->hasHtmlEmail()); - $this->assertFalse($result->isDisabled()); - } -} diff --git a/tests/Unit/Service/Manager/SubscriptionManagerTest.php b/tests/Unit/Service/Manager/SubscriptionManagerTest.php deleted file mode 100644 index 729deaa..0000000 --- a/tests/Unit/Service/Manager/SubscriptionManagerTest.php +++ /dev/null @@ -1,107 +0,0 @@ -subscriptionRepository = $this->createMock(SubscriptionRepository::class); - $this->subscriberRepository = $this->createMock(SubscriberRepository::class); - $this->manager = new SubscriptionManager($this->subscriptionRepository, $this->subscriberRepository); - } - - public function testCreateSubscriptionWhenSubscriberExists(): void - { - $email = 'test@example.com'; - $subscriber = new Subscriber(); - $list = new SubscriberList(); - - $this->subscriberRepository->method('findOneBy')->with(['email' => $email])->willReturn($subscriber); - $this->subscriptionRepository->method('findOneBySubscriberListAndSubscriber')->willReturn(null); - $this->subscriptionRepository->expects($this->once())->method('save'); - - $subscriptions = $this->manager->createSubscriptions($list, [$email]); - - $this->assertCount(1, $subscriptions); - $this->assertInstanceOf(Subscription::class, $subscriptions[0]); - } - - public function testCreateSubscriptionThrowsWhenSubscriberMissing(): void - { - $this->expectException(SubscriptionCreationException::class); - $this->expectExceptionMessage('Subscriber does not exists.'); - - $list = new SubscriberList(); - - $this->subscriberRepository->method('findOneBy')->willReturn(null); - - $this->manager->createSubscriptions($list, ['missing@example.com']); - } - - public function testDeleteSubscriptionSuccessfully(): void - { - $email = 'user@example.com'; - $subscriberList = $this->createMock(SubscriberList::class); - $subscriberList->method('getId')->willReturn(1); - $subscription = new Subscription(); - - $this->subscriptionRepository - ->method('findOneBySubscriberEmailAndListId') - ->with($subscriberList->getId(), $email) - ->willReturn($subscription); - - $this->subscriptionRepository->expects($this->once())->method('remove')->with($subscription); - - $this->manager->deleteSubscriptions($subscriberList, [$email]); - } - - public function testDeleteSubscriptionSkipsNotFound(): void - { - $email = 'missing@example.com'; - $subscriberList = $this->createMock(SubscriberList::class); - $subscriberList->method('getId')->willReturn(1); - - $this->subscriptionRepository - ->method('findOneBySubscriberEmailAndListId') - ->willReturn(null); - - $this->manager->deleteSubscriptions($subscriberList, [$email]); - - $this->addToAssertionCount(1); - } - - public function testGetSubscriberListMembersReturnsList(): void - { - $subscriberList = $this->createMock(SubscriberList::class); - $subscriberList->method('getId')->willReturn(1); - $subscriber = new Subscriber(); - - $this->subscriberRepository - ->method('getSubscribersBySubscribedListId') - ->with($subscriberList->getId()) - ->willReturn([$subscriber]); - - $result = $this->manager->getSubscriberListMembers($subscriberList); - - $this->assertIsArray($result); - $this->assertCount(1, $result); - $this->assertInstanceOf(Subscriber::class, $result[0]); - } -} diff --git a/tests/Unit/Service/Manager/TemplateImageManagerTest.php b/tests/Unit/Service/Manager/TemplateImageManagerTest.php deleted file mode 100644 index 32d26af..0000000 --- a/tests/Unit/Service/Manager/TemplateImageManagerTest.php +++ /dev/null @@ -1,88 +0,0 @@ -templateImageRepository = $this->createMock(TemplateImageRepository::class); - $this->entityManager = $this->createMock(EntityManagerInterface::class); - - $this->manager = new TemplateImageManager( - $this->templateImageRepository, - $this->entityManager - ); - } - - public function testCreateImagesFromImagePaths(): void - { - $template = $this->createMock(Template::class); - - $this->entityManager->expects($this->exactly(2)) - ->method('persist') - ->with($this->isInstanceOf(TemplateImage::class)); - - $this->entityManager->expects($this->once()) - ->method('flush'); - - $images = $this->manager->createImagesFromImagePaths(['image1.jpg', 'image2.png'], $template); - - $this->assertCount(2, $images); - foreach ($images as $image) { - $this->assertInstanceOf(TemplateImage::class, $image); - } - } - - public function testGuessMimeType(): void - { - $reflection = new \ReflectionClass($this->manager); - $method = $reflection->getMethod('guessMimeType'); - - $this->assertSame('image/jpeg', $method->invoke($this->manager, 'photo.jpg')); - $this->assertSame('image/png', $method->invoke($this->manager, 'picture.png')); - $this->assertSame('application/octet-stream', $method->invoke($this->manager, 'file.unknownext')); - } - - public function testExtractAllImages(): void - { - $html = '' . - '' . - '' . - '' . - 'Download' . - '' . - ''; - - $result = $this->manager->extractAllImages($html); - - $this->assertIsArray($result); - $this->assertContains('image1.jpg', $result); - $this->assertContains('https://example.com/image2.png', $result); - } - - public function testDeleteTemplateImage(): void - { - $templateImage = $this->createMock(TemplateImage::class); - - $this->templateImageRepository->expects($this->once()) - ->method('remove') - ->with($templateImage); - - $this->manager->delete($templateImage); - } -} diff --git a/tests/Unit/Service/Manager/TemplateManagerTest.php b/tests/Unit/Service/Manager/TemplateManagerTest.php deleted file mode 100644 index d021218..0000000 --- a/tests/Unit/Service/Manager/TemplateManagerTest.php +++ /dev/null @@ -1,90 +0,0 @@ -templateRepository = $this->createMock(TemplateRepository::class); - $entityManager = $this->createMock(EntityManagerInterface::class); - $this->templateImageManager = $this->createMock(TemplateImageManager::class); - $this->templateLinkValidator = $this->createMock(TemplateLinkValidator::class); - $this->templateImageValidator = $this->createMock(TemplateImageValidator::class); - - $this->manager = new TemplateManager( - $this->templateRepository, - $entityManager, - $this->templateImageManager, - $this->templateLinkValidator, - $this->templateImageValidator - ); - } - - public function testCreateTemplateSuccessfully(): void - { - $request = new CreateTemplateRequest(); - $request->title = 'Test Template'; - $request->content = 'Content'; - $request->text = 'Plain text'; - $request->checkLinks = true; - $request->checkImages = false; - $request->checkExternalImages = false; - $request->file = null; - - $this->templateLinkValidator->expects($this->once()) - ->method('validate') - ->with($request->content, $this->anything()); - - $this->templateImageManager->expects($this->once()) - ->method('extractAllImages') - ->with($request->content) - ->willReturn([]); - - $this->templateImageValidator->expects($this->once()) - ->method('validate') - ->with([], $this->anything()); - - $this->templateRepository->expects($this->once()) - ->method('save') - ->with($this->isInstanceOf(Template::class)); - - $this->templateImageManager->expects($this->once()) - ->method('createImagesFromImagePaths') - ->with([], $this->isInstanceOf(Template::class)); - - $template = $this->manager->create($request); - - $this->assertSame('Test Template', $template->getTitle()); - } - - public function testDeleteTemplate(): void - { - $template = $this->createMock(Template::class); - - $this->templateRepository->expects($this->once()) - ->method('remove') - ->with($template); - - $this->manager->delete($template); - } -} diff --git a/tests/Unit/Validator/TemplateImageValidatorTest.php b/tests/Unit/Validator/TemplateImageValidatorTest.php deleted file mode 100644 index 35bac9d..0000000 --- a/tests/Unit/Validator/TemplateImageValidatorTest.php +++ /dev/null @@ -1,86 +0,0 @@ -httpClient = $this->createMock(ClientInterface::class); - $this->validator = new TemplateImageValidator($this->httpClient); - } - - public function testThrowsExceptionIfValueIsNotArray(): void - { - $this->expectException(InvalidArgumentException::class); - - $this->validator->validate('not-an-array'); - } - - public function testValidatesFullUrls(): void - { - $context = (new ValidationContext())->set('checkImages', true); - - $this->expectException(ValidatorException::class); - $this->expectExceptionMessageMatches('/not-a-url/'); - - $this->validator->validate(['not-a-url', 'https://valid.url/image.jpg'], $context); - } - - public function testValidatesExistenceWithHttp200(): void - { - $context = (new ValidationContext())->set('checkExternalImages', true); - - $this->httpClient->expects($this->once()) - ->method('request') - ->with('HEAD', 'https://example.com/image.jpg') - ->willReturn(new Response(200)); - - $this->validator->validate(['https://example.com/image.jpg'], $context); - - $this->assertTrue(true); - } - - public function testValidatesExistenceWithHttp404(): void - { - $context = (new ValidationContext())->set('checkExternalImages', true); - - $this->httpClient->expects($this->once()) - ->method('request') - ->with('HEAD', 'https://example.com/missing.jpg') - ->willReturn(new Response(404)); - - $this->expectException(ValidatorException::class); - $this->expectExceptionMessageMatches('/does not exist/'); - - $this->validator->validate(['https://example.com/missing.jpg'], $context); - } - - public function testValidatesExistenceThrowsHttpException(): void - { - $context = (new ValidationContext())->set('checkExternalImages', true); - - $this->httpClient->expects($this->once()) - ->method('request') - ->willThrowException(new \Exception('Connection failed')); - - $this->expectException(ValidatorException::class); - $this->expectExceptionMessageMatches('/could not be validated/'); - - $this->validator->validate(['https://example.com/broken.jpg'], $context); - } -} diff --git a/tests/Unit/Validator/TemplateLinkValidatorTest.php b/tests/Unit/Validator/TemplateLinkValidatorTest.php deleted file mode 100644 index 78f8bd4..0000000 --- a/tests/Unit/Validator/TemplateLinkValidatorTest.php +++ /dev/null @@ -1,66 +0,0 @@ -validator = new TemplateLinkValidator(); - } - - public function testSkipsValidationIfNotString(): void - { - $context = (new ValidationContext())->set('checkLinks', true); - - $this->validator->validate(['not', 'a', 'string'], $context); - - $this->assertTrue(true); - } - - public function testSkipsValidationIfCheckLinksIsFalse(): void - { - $context = (new ValidationContext())->set('checkLinks', false); - - $this->validator->validate('Broken link', $context); - - $this->assertTrue(true); - } - - public function testValidatesInvalidLinks(): void - { - $context = (new ValidationContext())->set('checkLinks', true); - - $html = 'Broken'; - - $this->expectException(ValidatorException::class); - $this->expectExceptionMessageMatches('/invalid-link/'); - - $this->validator->validate($html, $context); - } - - public function testAllowsValidLinksAndPlaceholders(): void - { - $context = (new ValidationContext())->set('checkLinks', true); - - $html = '' . - 'Valid Link' . - 'Valid Link' . - 'Email Link' . - 'Placeholder' . - ''; - - $this->validator->validate($html, $context); - - $this->assertTrue(true); - } -} From 30c7451a21092cd066df6e35485da0722a64b85e Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 11 May 2025 13:35:13 +0400 Subject: [PATCH 02/21] ISSUE-345: attribute definition controller --- config/services/normalizers.yml | 4 + src/Controller/AdministratorController.php | 12 +- .../AttributeDefinitionController.php | 361 ++++++++++++++++++ .../CreateAttributeDefinitionRequest.php | 32 ++ .../UpdateAttributeDefinitionRequest.php | 32 ++ src/OpenApi/SwaggerSchemasResponseEntity.php | 39 +- .../AttributeDefinitionNormalizer.php | 39 ++ tests/Helpers/DummyRequestDto.php | 5 +- .../AttributeDefinitionControllerTest.php | 115 ++++++ .../AttributeDefinitionFixture.php | 27 ++ .../AttributeDefinitionNormalizerTest.php | 57 +++ 11 files changed, 694 insertions(+), 29 deletions(-) create mode 100644 src/Controller/AttributeDefinitionController.php create mode 100644 src/Entity/Request/CreateAttributeDefinitionRequest.php create mode 100644 src/Entity/Request/UpdateAttributeDefinitionRequest.php create mode 100644 src/Serializer/AttributeDefinitionNormalizer.php create mode 100644 tests/Integration/Controller/AttributeDefinitionControllerTest.php create mode 100644 tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php create mode 100644 tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php diff --git a/config/services/normalizers.yml b/config/services/normalizers.yml index ab3d3d7..afab1f7 100644 --- a/config/services/normalizers.yml +++ b/config/services/normalizers.yml @@ -43,5 +43,9 @@ services: tags: [ 'serializer.normalizer' ] autowire: true + PhpList\RestBundle\Serializer\AttributeDefinitionNormalizer: + tags: [ 'serializer.normalizer' ] + autowire: true + PhpList\RestBundle\Serializer\CursorPaginationNormalizer: autowire: true diff --git a/src/Controller/AdministratorController.php b/src/Controller/AdministratorController.php index 208e420..6dabab9 100644 --- a/src/Controller/AdministratorController.php +++ b/src/Controller/AdministratorController.php @@ -135,10 +135,10 @@ public function createAdministrator( ): JsonResponse { $this->requireAuthentication($request); - /** @var CreateAdministratorRequest $createAdministratorRequest */ - $createAdministratorRequest = $validator->validate($request, CreateAdministratorRequest::class); + /** @var CreateAdministratorRequest $createRequest */ + $createRequest = $validator->validate($request, CreateAdministratorRequest::class); - $administrator = $this->administratorManager->createAdministrator($createAdministratorRequest->getDto()); + $administrator = $this->administratorManager->createAdministrator($createRequest->getDto()); $json = $normalizer->normalize($administrator, 'json'); return $this->json($json, Response::HTTP_CREATED); @@ -225,9 +225,9 @@ public function updateAdministrator( if (!$administrator) { throw $this->createNotFoundException('Administrator not found.'); } - /** @var UpdateAdministratorRequest $updateAdministratorRequest */ - $updateAdministratorRequest = $this->validator->validate($request, UpdateAdministratorRequest::class); - $this->administratorManager->updateAdministrator($administrator, $updateAdministratorRequest->getDto()); + /** @var UpdateAdministratorRequest $updateRequest */ + $updateRequest = $this->validator->validate($request, UpdateAdministratorRequest::class); + $this->administratorManager->updateAdministrator($administrator, $updateRequest->getDto()); return $this->json(null, Response::HTTP_OK); } diff --git a/src/Controller/AttributeDefinitionController.php b/src/Controller/AttributeDefinitionController.php new file mode 100644 index 0000000..d1e96f2 --- /dev/null +++ b/src/Controller/AttributeDefinitionController.php @@ -0,0 +1,361 @@ +definitionManager = $definitionManager; + $this->normalizer = $normalizer; + $this->paginatedDataProvider = $paginatedDataProvider; + } + + #[Route('', name: 'create_attribute_definition', methods: ['POST'])] + #[OA\Post( + path: '/attributes', + description: '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( + 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: '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'), + ] + ) + ), + tags: ['attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema( + type: 'string' + ) + ) + ], + responses: [ + new OA\Response( + response: 201, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/AttributeDefinition') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 422, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse') + ), + ] + )] + public function create(Request $request,): JsonResponse + { + $this->requireAuthentication($request); + + /** @var CreateAttributeDefinitionRequest $definitionRequest */ + $definitionRequest = $this->validator->validate($request, CreateAttributeDefinitionRequest::class); + + $attributeDefinition = $this->definitionManager->create($definitionRequest->getDto()); + $json = $this->normalizer->normalize($attributeDefinition, 'json'); + + return $this->json($json, Response::HTTP_CREATED); + } + + #[Route('/{definitionId}', name: 'update_attribute_definition', methods: ['PUT'])] + #[OA\Put( + path: '/attributes/{definitionId}', + description: '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( + 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: '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'), + ] + ) + ), + tags: ['attributes'], + parameters: [ + new OA\Parameter( + name: 'definitionId', + description: 'Definition ID', + in: 'path', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/AttributeDefinition') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 422, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse') + ), + ] + )] + public function update( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?AttributeDefinition $attributeDefinition, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$attributeDefinition) { + throw $this->createNotFoundException('Attribute definition not found.'); + } + + /** @var UpdateAttributeDefinitionRequest $definitionRequest */ + $definitionRequest = $this->validator->validate($request, UpdateAttributeDefinitionRequest::class); + + $attributeDefinition = $this->definitionManager->update( + $attributeDefinition, + $definitionRequest->getDto(), + ); + $json = $this->normalizer->normalize($attributeDefinition, 'json'); + + return $this->json($json, Response::HTTP_OK); + } + + #[Route('/{definitionId}', name: 'delete_attribute_definition', methods: ['DELETE'])] + #[OA\Delete( + path: '/attributes/{definitionId}', + description: 'Deletes a single subscriber attribute definition.', + summary: 'Deletes an attribute definition.', + tags: ['attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'definitionId', + description: 'Definition ID', + in: 'path', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success' + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 404, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse') + ) + ] + )] + public function delete( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?AttributeDefinition $attributeDefinition, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$attributeDefinition) { + throw $this->createNotFoundException('Attribute definition not found.'); + } + + $this->definitionManager->delete($attributeDefinition); + + return $this->json(null, Response::HTTP_NO_CONTENT); + } + + #[Route('', name: 'get_attribute_definitions', methods: ['GET'])] + #[OA\Get( + path: '/attributes', + description: 'Returns a JSON list of all subscriber attribute definitions.', + summary: 'Gets a list of all subscriber attribute definitions.', + tags: ['attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema( + type: 'string' + ) + ), + new OA\Parameter( + name: 'after_id', + description: 'Last id (starting from 0)', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 1, minimum: 1) + ), + new OA\Parameter( + name: 'limit', + description: 'Number of results per page', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 25, maximum: 100, minimum: 1) + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'items', + type: 'array', + items: new OA\Items(ref: '#/components/schemas/AttributeDefinition') + ), + new OA\Property(property: 'pagination', ref: '#/components/schemas/CursorPagination') + ], + type: 'object' + ) + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ) + ] + )] + public function getPaginated(Request $request,): JsonResponse + { + $this->requireAuthentication($request); + + return $this->json( + $this->paginatedDataProvider->getPaginatedList($request, $this->normalizer, AttributeDefinition::class), + Response::HTTP_OK + ); + } + + #[Route('/{definitionId}', name: 'get_attribute_definition', methods: ['GET'])] + #[OA\Get( + path: '/attributes/{definitionId}', + description: 'Returns a single attribute with specified ID.', + summary: 'Gets attribute with specified ID.', + tags: ['attributes'], + parameters: [ + new OA\Parameter( + name: 'definitionId', + description: 'Definition ID', + in: 'path', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/AttributeDefinition') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 404, + description: 'Failure', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'message', + type: 'string', + example: 'There is no attribute with that ID.' + ) + ], + type: 'object' + ) + ) + ] + )] + public function getAttributeDefinition( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?AttributeDefinition $attributeDefinition, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$attributeDefinition) { + throw $this->createNotFoundException('Attribute definition not found.'); + } + + return $this->json( + $this->normalizer->normalize($attributeDefinition), + Response::HTTP_OK + ); + } +} diff --git a/src/Entity/Request/CreateAttributeDefinitionRequest.php b/src/Entity/Request/CreateAttributeDefinitionRequest.php new file mode 100644 index 0000000..c1b1f4c --- /dev/null +++ b/src/Entity/Request/CreateAttributeDefinitionRequest.php @@ -0,0 +1,32 @@ +name, + $this->type, + $this->order, + $this->defaultValue, + $this->required, + $this->tableName, + ); + } +} diff --git a/src/Entity/Request/UpdateAttributeDefinitionRequest.php b/src/Entity/Request/UpdateAttributeDefinitionRequest.php new file mode 100644 index 0000000..3f7ed65 --- /dev/null +++ b/src/Entity/Request/UpdateAttributeDefinitionRequest.php @@ -0,0 +1,32 @@ +name, + $this->type, + $this->order, + $this->defaultValue, + $this->required, + $this->tableName, + ); + } +} diff --git a/src/OpenApi/SwaggerSchemasResponseEntity.php b/src/OpenApi/SwaggerSchemasResponseEntity.php index faabf0d..d9652bd 100644 --- a/src/OpenApi/SwaggerSchemasResponseEntity.php +++ b/src/OpenApi/SwaggerSchemasResponseEntity.php @@ -177,27 +177,10 @@ #[OA\Schema( schema: 'Administrator', properties: [ - new OA\Property( - property: 'id', - type: 'integer', - example: 1 - ), - new OA\Property( - property: 'login_name', - type: 'string', - example: 'admin' - ), - new OA\Property( - property: 'email', - type: 'string', - format: 'email', - example: 'admin@example.com' - ), - new OA\Property( - property: 'super_user', - type: 'boolean', - example: true - ), + new OA\Property(property: 'id', type: 'integer', example: 1), + new OA\Property(property: 'login_name', type: 'string', example: 'admin'), + new OA\Property(property: 'email', type: 'string', format: 'email', example: 'admin@example.com'), + new OA\Property(property: 'super_user', type: 'boolean', example: true), new OA\Property( property: 'created_at', type: 'string', @@ -207,7 +190,19 @@ ], type: 'object' )] - +#[OA\Schema( + schema: 'AttributeDefinition', + 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: 'checkbox'), + 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: 'list_attributes'), + ], + type: 'object' +)] class SwaggerSchemasResponseEntity { } diff --git a/src/Serializer/AttributeDefinitionNormalizer.php b/src/Serializer/AttributeDefinitionNormalizer.php new file mode 100644 index 0000000..5f6c238 --- /dev/null +++ b/src/Serializer/AttributeDefinitionNormalizer.php @@ -0,0 +1,39 @@ + $object->getId(), + 'name' => $object->getName(), + 'type' => $object->getType(), + 'list_order' => $object->getListOrder(), + 'default_value' => $object->getDefaultValue(), + 'required' => $object->isRequired(), + 'table_name' => $object->getTableName(), + ]; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof AttributeDefinition; + } +} diff --git a/tests/Helpers/DummyRequestDto.php b/tests/Helpers/DummyRequestDto.php index e4ec8ff..00a695f 100644 --- a/tests/Helpers/DummyRequestDto.php +++ b/tests/Helpers/DummyRequestDto.php @@ -8,5 +8,8 @@ class DummyRequestDto implements RequestInterface { - + public function getDto(): mixed + { + return null; + } } diff --git a/tests/Integration/Controller/AttributeDefinitionControllerTest.php b/tests/Integration/Controller/AttributeDefinitionControllerTest.php new file mode 100644 index 0000000..1794ec1 --- /dev/null +++ b/tests/Integration/Controller/AttributeDefinitionControllerTest.php @@ -0,0 +1,115 @@ +get(AttributeDefinitionController::class) + ); + } + + public function testGetAttributesWithoutSessionKeyReturnsForbidden() + { + self::getClient()->request('GET', '/api/v2/attributes'); + $this->assertHttpForbidden(); + } + + public function testGetAttributesWithSessionKeyReturnsOk() + { + $this->loadFixtures([AdministratorFixture::class, AdministratorTokenFixture::class]); + $this->authenticatedJsonRequest('GET', '/api/v2/attributes'); + $this->assertHttpOkay(); + } + + public function testGetAttributeWithInvalidIdReturnsNotFound() + { + $this->loadFixtures([AdministratorFixture::class, AdministratorTokenFixture::class]); + $this->authenticatedJsonRequest('GET', '/api/v2/attributes/999'); + $this->assertHttpNotFound(); + } + + public function testCreateAttributeDefinition() + { + $this->loadFixtures([AdministratorFixture::class, AdministratorTokenFixture::class]); + + $payload = json_encode([ + 'name' => 'Country', + 'type' => 'checkbox', + 'order' => 12, + 'default_value' => 'United States', + 'required' => true, + 'table_name' => 'list_attributes', + ]); + + $this->authenticatedJsonRequest('POST', '/api/v2/attributes', [], [], [], $payload); + + $this->assertHttpCreated(); + + $response = $this->getDecodedJsonResponseContent(); + self::assertSame('Country', $response['name']); + } + + public function testUpdateAttributeDefinition() + { + $this->loadFixtures([ + AdministratorFixture::class, + AdministratorTokenFixture::class, + AttributeDefinitionFixture::class, + ]); + + $payload = json_encode([ + 'name' => 'Updated Country', + 'type' => 'checkbox', + 'order' => 10, + 'default_value' => 'Canada', + 'required' => false, + 'table_name' => 'list_attributes', + ]); + + $this->authenticatedJsonRequest('PUT', '/api/v2/attributes/1', [], [], [], $payload); + $this->assertHttpOkay(); + $response = $this->getDecodedJsonResponseContent(); + self::assertSame('Updated Country', $response['name']); + } + + public function testDeleteAttributeDefinition() + { + $this->loadFixtures([ + AdministratorFixture::class, + AdministratorTokenFixture::class, + AttributeDefinitionFixture::class, + ]); + + $this->authenticatedJsonRequest('DELETE', '/api/v2/attributes/1'); + $this->assertHttpNoContent(); + + $repo = self::getContainer()->get(AttributeDefinitionRepository::class); + self::assertNull($repo->find(1)); + } + + public function testCreateAttributeDefinitionMissingNameReturnsValidationError(): void + { + $this->loadFixtures([AdministratorFixture::class, AdministratorTokenFixture::class]); + + $payload = json_encode([ + 'type' => 'text', + 'order' => 1, + 'required' => false + ]); + + $this->authenticatedJsonRequest('POST', '/api/v2/attributes', [], [], [], $payload); + $this->assertHttpUnprocessableEntity(); + } +} diff --git a/tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php b/tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php new file mode 100644 index 0000000..afc70a8 --- /dev/null +++ b/tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php @@ -0,0 +1,27 @@ +setName('Country'); + $definition->setType('checkbox'); + $definition->setListOrder(1); + $definition->setDefaultValue('US'); + $definition->setRequired(true); + $definition->setTableName('list_attributes'); + + $manager->persist($definition); + $manager->flush(); + } +} diff --git a/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php b/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php new file mode 100644 index 0000000..744f89a --- /dev/null +++ b/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php @@ -0,0 +1,57 @@ +createMock(AttributeDefinition::class); + self::assertTrue($normalizer->supportsNormalization($definition)); + + $nonSupported = new \stdClass(); + self::assertFalse($normalizer->supportsNormalization($nonSupported)); + } + + public function testNormalize(): void + { + $definition = $this->createMock(AttributeDefinition::class); + $definition->method('getId')->willReturn(1); + $definition->method('getName')->willReturn('Country'); + $definition->method('getType')->willReturn('text'); + $definition->method('getListOrder')->willReturn(12); + $definition->method('getDefaultValue')->willReturn('US'); + $definition->method('isRequired')->willReturn(true); + $definition->method('getTableName')->willReturn('user_attribute'); + + $normalizer = new AttributeDefinitionNormalizer(); + $result = $normalizer->normalize($definition); + + self::assertIsArray($result); + self::assertSame([ + 'id' => 1, + 'name' => 'Country', + 'type' => 'text', + 'list_order' => 12, + 'default_value' => 'US', + 'required' => true, + 'table_name' => 'user_attribute', + ], $result); + } + + public function testNormalizeWithInvalidObjectReturnsEmptyArray(): void + { + $normalizer = new AttributeDefinitionNormalizer(); + $result = $normalizer->normalize(new \stdClass()); + + self::assertSame([], $result); + } +} From ed89e03466d504d6cc263a5dd8ca1ac938da1802 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 11 May 2025 14:50:21 +0400 Subject: [PATCH 03/21] ISSUE-345: naming --- src/Controller/AttributeDefinitionController.php | 14 +++++++++----- src/Serializer/AttributeDefinitionNormalizer.php | 6 +++--- .../AttributeDefinitionControllerTest.php | 4 ++-- .../Subscription/AttributeDefinitionFixture.php | 4 ++-- .../AttributeDefinitionNormalizerTest.php | 6 +++--- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Controller/AttributeDefinitionController.php b/src/Controller/AttributeDefinitionController.php index d1e96f2..23b52cc 100644 --- a/src/Controller/AttributeDefinitionController.php +++ b/src/Controller/AttributeDefinitionController.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Subscription\AttributeDefinition; +use PhpList\Core\Domain\Model\Subscription\SubscriberAttributeDefinition; use PhpList\Core\Domain\Service\Manager\AttributeDefinitionManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateAttributeDefinitionRequest; @@ -159,7 +159,7 @@ public function create(Request $request,): JsonResponse )] public function update( Request $request, - #[MapEntity(mapping: ['definitionId' => 'id'])] ?AttributeDefinition $attributeDefinition, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?SubscriberAttributeDefinition $attributeDefinition, ): JsonResponse { $this->requireAuthentication($request); if (!$attributeDefinition) { @@ -219,7 +219,7 @@ public function update( )] public function delete( Request $request, - #[MapEntity(mapping: ['definitionId' => 'id'])] ?AttributeDefinition $attributeDefinition, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?SubscriberAttributeDefinition $attributeDefinition, ): JsonResponse { $this->requireAuthentication($request); if (!$attributeDefinition) { @@ -290,7 +290,11 @@ public function getPaginated(Request $request,): JsonResponse $this->requireAuthentication($request); return $this->json( - $this->paginatedDataProvider->getPaginatedList($request, $this->normalizer, AttributeDefinition::class), + $this->paginatedDataProvider->getPaginatedList( + $request, + $this->normalizer, + SubscriberAttributeDefinition::class, + ), Response::HTTP_OK ); } @@ -346,7 +350,7 @@ public function getPaginated(Request $request,): JsonResponse )] public function getAttributeDefinition( Request $request, - #[MapEntity(mapping: ['definitionId' => 'id'])] ?AttributeDefinition $attributeDefinition, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?SubscriberAttributeDefinition $attributeDefinition, ): JsonResponse { $this->requireAuthentication($request); if (!$attributeDefinition) { diff --git a/src/Serializer/AttributeDefinitionNormalizer.php b/src/Serializer/AttributeDefinitionNormalizer.php index 5f6c238..441443a 100644 --- a/src/Serializer/AttributeDefinitionNormalizer.php +++ b/src/Serializer/AttributeDefinitionNormalizer.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Serializer; -use PhpList\Core\Domain\Model\Subscription\AttributeDefinition; +use PhpList\Core\Domain\Model\Subscription\SubscriberAttributeDefinition; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class AttributeDefinitionNormalizer implements NormalizerInterface @@ -14,7 +14,7 @@ class AttributeDefinitionNormalizer implements NormalizerInterface */ public function normalize($object, string $format = null, array $context = []): array { - if (!$object instanceof AttributeDefinition) { + if (!$object instanceof SubscriberAttributeDefinition) { return []; } @@ -34,6 +34,6 @@ public function normalize($object, string $format = null, array $context = []): */ public function supportsNormalization($data, string $format = null): bool { - return $data instanceof AttributeDefinition; + return $data instanceof SubscriberAttributeDefinition; } } diff --git a/tests/Integration/Controller/AttributeDefinitionControllerTest.php b/tests/Integration/Controller/AttributeDefinitionControllerTest.php index 1794ec1..d0256fa 100644 --- a/tests/Integration/Controller/AttributeDefinitionControllerTest.php +++ b/tests/Integration/Controller/AttributeDefinitionControllerTest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Tests\Integration\Controller; -use PhpList\Core\Domain\Repository\Subscription\AttributeDefinitionRepository; +use PhpList\Core\Domain\Repository\Subscription\SubscriberAttributeDefinitionRepository; use PhpList\RestBundle\Controller\AttributeDefinitionController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; @@ -95,7 +95,7 @@ public function testDeleteAttributeDefinition() $this->authenticatedJsonRequest('DELETE', '/api/v2/attributes/1'); $this->assertHttpNoContent(); - $repo = self::getContainer()->get(AttributeDefinitionRepository::class); + $repo = self::getContainer()->get(SubscriberAttributeDefinitionRepository::class); self::assertNull($repo->find(1)); } diff --git a/tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php b/tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php index afc70a8..3403327 100644 --- a/tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php +++ b/tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php @@ -7,13 +7,13 @@ use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Common\DataFixtures\FixtureInterface; use Doctrine\Persistence\ObjectManager; -use PhpList\Core\Domain\Model\Subscription\AttributeDefinition; +use PhpList\Core\Domain\Model\Subscription\SubscriberAttributeDefinition; class AttributeDefinitionFixture extends Fixture implements FixtureInterface { public function load(ObjectManager $manager): void { - $definition = new AttributeDefinition(); + $definition = new SubscriberAttributeDefinition(); $definition->setName('Country'); $definition->setType('checkbox'); $definition->setListOrder(1); diff --git a/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php b/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php index 744f89a..02c5ddc 100644 --- a/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php +++ b/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; -use PhpList\Core\Domain\Model\Subscription\AttributeDefinition; +use PhpList\Core\Domain\Model\Subscription\SubscriberAttributeDefinition; use PhpList\RestBundle\Serializer\AttributeDefinitionNormalizer; use PHPUnit\Framework\TestCase; @@ -14,7 +14,7 @@ public function testSupportsNormalization(): void { $normalizer = new AttributeDefinitionNormalizer(); - $definition = $this->createMock(AttributeDefinition::class); + $definition = $this->createMock(SubscriberAttributeDefinition::class); self::assertTrue($normalizer->supportsNormalization($definition)); $nonSupported = new \stdClass(); @@ -23,7 +23,7 @@ public function testSupportsNormalization(): void public function testNormalize(): void { - $definition = $this->createMock(AttributeDefinition::class); + $definition = $this->createMock(SubscriberAttributeDefinition::class); $definition->method('getId')->willReturn(1); $definition->method('getName')->willReturn('Country'); $definition->method('getType')->willReturn('text'); From 6a6974a90163a5639f8784194d18967e64897f73 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 11 May 2025 18:11:32 +0400 Subject: [PATCH 04/21] ISSUE-345: subscriber attribute value --- config/services/normalizers.yml | 4 ++ ...bscriberAttributeDefinitionController.php} | 2 +- .../SubscriberAttributeValueNormalizer.php | 41 +++++++++++++++++++ .../AttributeDefinitionControllerTest.php | 6 +-- 4 files changed, 49 insertions(+), 4 deletions(-) rename src/Controller/{AttributeDefinitionController.php => SubscriberAttributeDefinitionController.php} (99%) create mode 100644 src/Serializer/SubscriberAttributeValueNormalizer.php diff --git a/config/services/normalizers.yml b/config/services/normalizers.yml index afab1f7..d2849df 100644 --- a/config/services/normalizers.yml +++ b/config/services/normalizers.yml @@ -47,5 +47,9 @@ services: tags: [ 'serializer.normalizer' ] autowire: true + PhpList\RestBundle\Serializer\SubscriberAttributeValueNormalizer: + tags: [ 'serializer.normalizer' ] + autowire: true + PhpList\RestBundle\Serializer\CursorPaginationNormalizer: autowire: true diff --git a/src/Controller/AttributeDefinitionController.php b/src/Controller/SubscriberAttributeDefinitionController.php similarity index 99% rename from src/Controller/AttributeDefinitionController.php rename to src/Controller/SubscriberAttributeDefinitionController.php index 23b52cc..d2be9c7 100644 --- a/src/Controller/AttributeDefinitionController.php +++ b/src/Controller/SubscriberAttributeDefinitionController.php @@ -20,7 +20,7 @@ use Symfony\Component\Routing\Attribute\Route; #[Route('/attributes')] -class AttributeDefinitionController extends BaseController +class SubscriberAttributeDefinitionController extends BaseController { private AttributeDefinitionManager $definitionManager; private AttributeDefinitionNormalizer $normalizer; diff --git a/src/Serializer/SubscriberAttributeValueNormalizer.php b/src/Serializer/SubscriberAttributeValueNormalizer.php new file mode 100644 index 0000000..ceefc86 --- /dev/null +++ b/src/Serializer/SubscriberAttributeValueNormalizer.php @@ -0,0 +1,41 @@ + $this->subscriberNormalizer->normalize($object->getSubscriber()), + 'definition' => $this->attributeDefinitionNormalizer->normalize($object->getAttributeDefinition()), + 'value' => $object->getValue(), + ]; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof SubscriberAttributeValue; + } +} diff --git a/tests/Integration/Controller/AttributeDefinitionControllerTest.php b/tests/Integration/Controller/AttributeDefinitionControllerTest.php index d0256fa..807e1fc 100644 --- a/tests/Integration/Controller/AttributeDefinitionControllerTest.php +++ b/tests/Integration/Controller/AttributeDefinitionControllerTest.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Tests\Integration\Controller; use PhpList\Core\Domain\Repository\Subscription\SubscriberAttributeDefinitionRepository; -use PhpList\RestBundle\Controller\AttributeDefinitionController; +use PhpList\RestBundle\Controller\SubscriberAttributeDefinitionController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\AttributeDefinitionFixture; @@ -15,8 +15,8 @@ class AttributeDefinitionControllerTest extends AbstractTestController public function testControllerIsAvailableViaContainer() { self::assertInstanceOf( - AttributeDefinitionController::class, - self::getContainer()->get(AttributeDefinitionController::class) + SubscriberAttributeDefinitionController::class, + self::getContainer()->get(SubscriberAttributeDefinitionController::class) ); } From 2a13beb6f968411dd4265ef3a31d0ca716c8129f Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 11 May 2025 19:21:25 +0400 Subject: [PATCH 05/21] ISSUE-345: update after core update --- config/services/builders.yml | 10 +++++----- config/services/managers.yml | 16 ++++++++-------- src/Controller/AdministratorController.php | 4 ++-- src/Controller/BaseController.php | 2 +- src/Controller/CampaignController.php | 6 +++--- src/Controller/ListController.php | 4 ++-- src/Controller/ListMembersController.php | 6 +++--- src/Controller/SessionController.php | 4 ++-- ...ubscriberAttributeDefinitionController.php | 4 ++-- src/Controller/SubscriberController.php | 4 ++-- src/Controller/SubscriptionController.php | 4 ++-- src/Controller/TemplateController.php | 4 ++-- .../Request/CreateAdministratorRequest.php | 4 ++-- .../CreateAttributeDefinitionRequest.php | 2 +- src/Entity/Request/CreateMessageRequest.php | 4 ++-- .../Request/CreateSubscriberListRequest.php | 2 +- .../Request/CreateSubscriberRequest.php | 4 ++-- src/Entity/Request/CreateTemplateRequest.php | 2 +- .../Request/Message/MessageContentRequest.php | 2 +- .../Request/Message/MessageFormatRequest.php | 2 +- .../Message/MessageMetadataRequest.php | 2 +- .../Request/Message/MessageOptionsRequest.php | 2 +- .../Message/MessageScheduleRequest.php | 2 +- .../Request/Message/RequestDtoInterface.php | 2 ++ .../Request/UpdateAdministratorRequest.php | 4 ++-- .../UpdateAttributeDefinitionRequest.php | 2 +- src/Entity/Request/UpdateMessageRequest.php | 4 ++-- .../Request/UpdateSubscriberRequest.php | 4 ++-- src/EventListener/ExceptionListener.php | 2 +- src/Serializer/AdministratorNormalizer.php | 2 +- .../AdministratorTokenNormalizer.php | 2 +- .../AttributeDefinitionNormalizer.php | 2 +- src/Serializer/MessageNormalizer.php | 2 +- .../SubscriberAttributeValueNormalizer.php | 6 +++--- src/Serializer/SubscriberListNormalizer.php | 2 +- src/Serializer/SubscriberNormalizer.php | 4 ++-- src/Serializer/SubscriptionNormalizer.php | 2 +- src/Serializer/TemplateImageNormalizer.php | 2 +- src/Serializer/TemplateNormalizer.php | 4 ++-- .../Provider/PaginatedDataProvider.php | 4 ++-- .../Constraint/EmailExistsValidator.php | 2 +- .../Constraint/TemplateExistsValidator.php | 2 +- .../Constraint/UniqueLoginNameValidator.php | 2 +- tests/Helpers/DummyPaginatableRepository.php | 4 ++-- .../AttributeDefinitionControllerTest.php | 2 +- .../Identity/AdministratorFixture.php | 2 +- .../Identity/AdministratorTokenFixture.php | 4 ++-- .../Fixtures/Messaging/MessageFixture.php | 16 ++++++++-------- .../Messaging/SubscriberListFixture.php | 4 ++-- .../Fixtures/Messaging/TemplateFixture.php | 4 +++- .../AttributeDefinitionFixture.php | 2 +- .../Subscription/SubscriberFixture.php | 2 +- .../Subscription/SubscriptionFixture.php | 6 +++--- .../Controller/ListControllerTest.php | 2 +- .../Controller/SessionControllerTest.php | 4 ++-- .../Controller/SubscriberControllerTest.php | 4 ++-- .../Controller/TemplateControllerTest.php | 2 +- .../EventListener/ExceptionListenerTest.php | 2 +- .../AdministratorNormalizerTest.php | 2 +- .../AdministratorTokenNormalizerTest.php | 2 +- .../AttributeDefinitionNormalizerTest.php | 2 +- .../Unit/Serializer/MessageNormalizerTest.php | 19 +++++++------------ .../SubscriberListNormalizerTest.php | 2 +- .../Serializer/SubscriberNormalizerTest.php | 6 +++--- .../Serializer/SubscriptionNormalizerTest.php | 6 +++--- .../TemplateImageNormalizerTest.php | 4 ++-- .../Serializer/TemplateNormalizerTest.php | 4 ++-- .../Constraint/EmailExistsValidatorTest.php | 4 ++-- .../TemplateExistsValidatorTest.php | 4 ++-- .../Constraint/UniqueEmailValidatorTest.php | 4 ++-- .../UniqueLoginNameValidatorTest.php | 4 ++-- 71 files changed, 137 insertions(+), 138 deletions(-) diff --git a/config/services/builders.yml b/config/services/builders.yml index 655e3d6..10a994a 100644 --- a/config/services/builders.yml +++ b/config/services/builders.yml @@ -4,22 +4,22 @@ services: autoconfigure: true public: false - PhpList\Core\Domain\Service\Builder\MessageBuilder: + PhpList\Core\Domain\Messaging\Service\Builder\MessageBuilder: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Builder\MessageFormatBuilder: + PhpList\Core\Domain\Messaging\Service\Builder\MessageFormatBuilder: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Builder\MessageScheduleBuilder: + PhpList\Core\Domain\Messaging\Service\Builder\MessageScheduleBuilder: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Builder\MessageContentBuilder: + PhpList\Core\Domain\Messaging\Service\Builder\MessageContentBuilder: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Builder\MessageOptionsBuilder: + PhpList\Core\Domain\Messaging\Service\Builder\MessageOptionsBuilder: autowire: true autoconfigure: true diff --git a/config/services/managers.yml b/config/services/managers.yml index 1bf9380..25dbfe9 100644 --- a/config/services/managers.yml +++ b/config/services/managers.yml @@ -4,34 +4,34 @@ services: autoconfigure: true public: false - PhpList\Core\Domain\Service\Manager\SubscriberManager: + PhpList\Core\Domain\Subscription\Service\SubscriberManager: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Manager\SessionManager: + PhpList\Core\Domain\Identity\Service\SessionManager: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Manager\SubscriberListManager: + PhpList\Core\Domain\Subscription\Service\SubscriberListManager: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Manager\SubscriptionManager: + PhpList\Core\Domain\Subscription\Service\SubscriptionManager: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Manager\MessageManager: + PhpList\Core\Domain\Messaging\Service\MessageManager: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Manager\TemplateManager: + PhpList\Core\Domain\Messaging\Service\TemplateManager: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Manager\TemplateImageManager: + PhpList\Core\Domain\Messaging\Service\TemplateImageManager: autowire: true autoconfigure: true - PhpList\Core\Domain\Service\Manager\AdministratorManager: + PhpList\Core\Domain\Udentity\Service\AdministratorManager: autowire: true autoconfigure: true diff --git a/src/Controller/AdministratorController.php b/src/Controller/AdministratorController.php index 6dabab9..881e59e 100644 --- a/src/Controller/AdministratorController.php +++ b/src/Controller/AdministratorController.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Identity\Administrator; -use PhpList\Core\Domain\Service\Manager\AdministratorManager; +use PhpList\Core\Domain\Identity\Model\Administrator; +use PhpList\Core\Domain\Identity\Service\AdministratorManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateAdministratorRequest; use PhpList\RestBundle\Entity\Request\UpdateAdministratorRequest; diff --git a/src/Controller/BaseController.php b/src/Controller/BaseController.php index 6b84672..5e0a950 100644 --- a/src/Controller/BaseController.php +++ b/src/Controller/BaseController.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Controller; -use PhpList\Core\Domain\Model\Identity\Administrator; +use PhpList\Core\Domain\Identity\Model\Administrator; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Validator\RequestValidator; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; diff --git a/src/Controller/CampaignController.php b/src/Controller/CampaignController.php index 071a446..e8c0a62 100644 --- a/src/Controller/CampaignController.php +++ b/src/Controller/CampaignController.php @@ -5,9 +5,9 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Dto\Filter\MessageFilter; -use PhpList\Core\Domain\Model\Messaging\Message; -use PhpList\Core\Domain\Service\Manager\MessageManager; +use PhpList\Core\Domain\Messaging\Model\Filter\MessageFilter; +use PhpList\Core\Domain\Messaging\Model\Message; +use PhpList\Core\Domain\Messaging\Service\MessageManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateMessageRequest; use PhpList\RestBundle\Entity\Request\UpdateMessageRequest; diff --git a/src/Controller/ListController.php b/src/Controller/ListController.php index af9f9b3..eafea3a 100644 --- a/src/Controller/ListController.php +++ b/src/Controller/ListController.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Subscription\SubscriberList; -use PhpList\Core\Domain\Service\Manager\SubscriberListManager; +use PhpList\Core\Domain\Subscription\Model\SubscriberList; +use PhpList\Core\Domain\Subscription\Service\SubscriberListManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateSubscriberListRequest; use PhpList\RestBundle\Serializer\SubscriberListNormalizer; diff --git a/src/Controller/ListMembersController.php b/src/Controller/ListMembersController.php index 0fae297..5aa65a0 100644 --- a/src/Controller/ListMembersController.php +++ b/src/Controller/ListMembersController.php @@ -5,9 +5,9 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Dto\Filter\SubscriberFilter; -use PhpList\Core\Domain\Model\Subscription\Subscriber; -use PhpList\Core\Domain\Model\Subscription\SubscriberList; +use PhpList\Core\Domain\Subscription\Model\Filter\SubscriberFilter; +use PhpList\Core\Domain\Subscription\Model\Subscriber; +use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Serializer\SubscriberNormalizer; use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; diff --git a/src/Controller/SessionController.php b/src/Controller/SessionController.php index 879c045..83cc05f 100644 --- a/src/Controller/SessionController.php +++ b/src/Controller/SessionController.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Identity\AdministratorToken; -use PhpList\Core\Domain\Service\Manager\SessionManager; +use PhpList\Core\Domain\Identity\Model\AdministratorToken; +use PhpList\Core\Domain\Identity\Service\SessionManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateSessionRequest; use PhpList\RestBundle\Serializer\AdministratorTokenNormalizer; diff --git a/src/Controller/SubscriberAttributeDefinitionController.php b/src/Controller/SubscriberAttributeDefinitionController.php index d2be9c7..195b529 100644 --- a/src/Controller/SubscriberAttributeDefinitionController.php +++ b/src/Controller/SubscriberAttributeDefinitionController.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Subscription\SubscriberAttributeDefinition; -use PhpList\Core\Domain\Service\Manager\AttributeDefinitionManager; +use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition; +use PhpList\Core\Domain\Subscription\Service\AttributeDefinitionManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateAttributeDefinitionRequest; use PhpList\RestBundle\Entity\Request\UpdateAttributeDefinitionRequest; diff --git a/src/Controller/SubscriberController.php b/src/Controller/SubscriberController.php index b233fc7..8a4fb9c 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Controller/SubscriberController.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Subscription\Subscriber; -use PhpList\Core\Domain\Service\Manager\SubscriberManager; +use PhpList\Core\Domain\Subscription\Model\Subscriber; +use PhpList\Core\Domain\Subscription\Service\SubscriberManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateSubscriberRequest; use PhpList\RestBundle\Entity\Request\UpdateSubscriberRequest; diff --git a/src/Controller/SubscriptionController.php b/src/Controller/SubscriptionController.php index 2bf4e8b..c9a4e95 100644 --- a/src/Controller/SubscriptionController.php +++ b/src/Controller/SubscriptionController.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Subscription\SubscriberList; -use PhpList\Core\Domain\Service\Manager\SubscriptionManager; +use PhpList\Core\Domain\Subscription\Model\SubscriberList; +use PhpList\Core\Domain\Subscription\Service\SubscriptionManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\SubscriptionRequest; use PhpList\RestBundle\Serializer\SubscriptionNormalizer; diff --git a/src/Controller/TemplateController.php b/src/Controller/TemplateController.php index f9e8d42..b18bcc8 100644 --- a/src/Controller/TemplateController.php +++ b/src/Controller/TemplateController.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Model\Messaging\Template; -use PhpList\Core\Domain\Service\Manager\TemplateManager; +use PhpList\Core\Domain\Messaging\Model\Template; +use PhpList\Core\Domain\Messaging\Service\TemplateManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Entity\Request\CreateTemplateRequest; use PhpList\RestBundle\Serializer\TemplateNormalizer; diff --git a/src/Entity/Request/CreateAdministratorRequest.php b/src/Entity/Request/CreateAdministratorRequest.php index 7d6ad2a..b9161be 100644 --- a/src/Entity/Request/CreateAdministratorRequest.php +++ b/src/Entity/Request/CreateAdministratorRequest.php @@ -4,10 +4,10 @@ namespace PhpList\RestBundle\Entity\Request; -use PhpList\Core\Domain\Model\Identity\Administrator; +use PhpList\Core\Domain\Identity\Model\Administrator; +use PhpList\Core\Domain\Identity\Model\Dto\CreateAdministratorDto; use Symfony\Component\Validator\Constraints as Assert; use PhpList\RestBundle\Validator\Constraint as CustomAssert; -use PhpList\Core\Domain\Model\Identity\Dto\CreateAdministratorDto; class CreateAdministratorRequest implements RequestInterface { diff --git a/src/Entity/Request/CreateAttributeDefinitionRequest.php b/src/Entity/Request/CreateAttributeDefinitionRequest.php index c1b1f4c..d34d657 100644 --- a/src/Entity/Request/CreateAttributeDefinitionRequest.php +++ b/src/Entity/Request/CreateAttributeDefinitionRequest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Entity\Request; -use PhpList\Core\Domain\Model\Subscription\Dto\AttributeDefinitionDto; +use PhpList\Core\Domain\Subscription\Model\Dto\AttributeDefinitionDto; use Symfony\Component\Validator\Constraints as Assert; class CreateAttributeDefinitionRequest implements RequestInterface diff --git a/src/Entity/Request/CreateMessageRequest.php b/src/Entity/Request/CreateMessageRequest.php index c9621d7..8f6f5e2 100644 --- a/src/Entity/Request/CreateMessageRequest.php +++ b/src/Entity/Request/CreateMessageRequest.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Entity\Request; -use PhpList\Core\Domain\Model\Messaging\Dto\CreateMessageDto; -use PhpList\Core\Domain\Model\Messaging\Dto\MessageDtoInterface; +use PhpList\Core\Domain\Messaging\Model\Dto\CreateMessageDto; +use PhpList\Core\Domain\Messaging\Model\Dto\MessageDtoInterface; use PhpList\RestBundle\Entity\Request\Message\MessageContentRequest; use PhpList\RestBundle\Entity\Request\Message\MessageFormatRequest; use PhpList\RestBundle\Entity\Request\Message\MessageMetadataRequest; diff --git a/src/Entity/Request/CreateSubscriberListRequest.php b/src/Entity/Request/CreateSubscriberListRequest.php index 7857f44..74bbb20 100644 --- a/src/Entity/Request/CreateSubscriberListRequest.php +++ b/src/Entity/Request/CreateSubscriberListRequest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Entity\Request; -use PhpList\Core\Domain\Model\Subscription\Dto\CreateSubscriberListDto; +use PhpList\Core\Domain\Subscription\Model\Dto\CreateSubscriberListDto; use Symfony\Component\Validator\Constraints as Assert; class CreateSubscriberListRequest implements RequestInterface diff --git a/src/Entity/Request/CreateSubscriberRequest.php b/src/Entity/Request/CreateSubscriberRequest.php index 6112fdb..b27adf4 100644 --- a/src/Entity/Request/CreateSubscriberRequest.php +++ b/src/Entity/Request/CreateSubscriberRequest.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Entity\Request; -use PhpList\Core\Domain\Model\Subscription\Dto\CreateSubscriberDto; -use PhpList\Core\Domain\Model\Subscription\Subscriber; +use PhpList\Core\Domain\Subscription\Model\Dto\CreateSubscriberDto; +use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\RestBundle\Validator\Constraint as CustomAssert; use Symfony\Component\Validator\Constraints as Assert; diff --git a/src/Entity/Request/CreateTemplateRequest.php b/src/Entity/Request/CreateTemplateRequest.php index 3f8a988..b68e31c 100644 --- a/src/Entity/Request/CreateTemplateRequest.php +++ b/src/Entity/Request/CreateTemplateRequest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Entity\Request; -use PhpList\Core\Domain\Model\Messaging\Dto\CreateTemplateDto; +use PhpList\Core\Domain\Messaging\Model\Dto\CreateTemplateDto; use PhpList\RestBundle\Validator\Constraint as CustomAssert; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\Validator\Constraints as Assert; diff --git a/src/Entity/Request/Message/MessageContentRequest.php b/src/Entity/Request/Message/MessageContentRequest.php index 28ce0a2..ba2c616 100644 --- a/src/Entity/Request/Message/MessageContentRequest.php +++ b/src/Entity/Request/Message/MessageContentRequest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Entity\Request\Message; -use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageContentDto; +use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageContentDto; use Symfony\Component\Validator\Constraints as Assert; class MessageContentRequest implements RequestDtoInterface diff --git a/src/Entity/Request/Message/MessageFormatRequest.php b/src/Entity/Request/Message/MessageFormatRequest.php index dd95a96..2a6f429 100644 --- a/src/Entity/Request/Message/MessageFormatRequest.php +++ b/src/Entity/Request/Message/MessageFormatRequest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Entity\Request\Message; -use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageFormatDto; +use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageFormatDto; use Symfony\Component\Validator\Constraints as Assert; class MessageFormatRequest implements RequestDtoInterface diff --git a/src/Entity/Request/Message/MessageMetadataRequest.php b/src/Entity/Request/Message/MessageMetadataRequest.php index 4e910c2..d16d962 100644 --- a/src/Entity/Request/Message/MessageMetadataRequest.php +++ b/src/Entity/Request/Message/MessageMetadataRequest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Entity\Request\Message; -use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageMetadataDto; +use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageMetadataDto; use Symfony\Component\Validator\Constraints as Assert; class MessageMetadataRequest implements RequestDtoInterface diff --git a/src/Entity/Request/Message/MessageOptionsRequest.php b/src/Entity/Request/Message/MessageOptionsRequest.php index 2c67f84..044feb8 100644 --- a/src/Entity/Request/Message/MessageOptionsRequest.php +++ b/src/Entity/Request/Message/MessageOptionsRequest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Entity\Request\Message; -use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageOptionsDto; +use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageOptionsDto; use Symfony\Component\Validator\Constraints as Assert; class MessageOptionsRequest implements RequestDtoInterface diff --git a/src/Entity/Request/Message/MessageScheduleRequest.php b/src/Entity/Request/Message/MessageScheduleRequest.php index 49226fc..860f40e 100644 --- a/src/Entity/Request/Message/MessageScheduleRequest.php +++ b/src/Entity/Request/Message/MessageScheduleRequest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Entity\Request\Message; -use PhpList\Core\Domain\Model\Messaging\Dto\Message\MessageScheduleDto; +use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageScheduleDto; use Symfony\Component\Validator\Constraints as Assert; class MessageScheduleRequest implements RequestDtoInterface diff --git a/src/Entity/Request/Message/RequestDtoInterface.php b/src/Entity/Request/Message/RequestDtoInterface.php index 403344d..d4a723a 100644 --- a/src/Entity/Request/Message/RequestDtoInterface.php +++ b/src/Entity/Request/Message/RequestDtoInterface.php @@ -1,5 +1,7 @@ $this->subscriberNormalizer->normalize($object->getSubscriber()), - 'definition' => $this->attributeDefinitionNormalizer->normalize($object->getAttributeDefinition()), + 'definition' => $this->definitionNormalizer->normalize($object->getAttributeDefinition()), 'value' => $object->getValue(), ]; } diff --git a/src/Serializer/SubscriberListNormalizer.php b/src/Serializer/SubscriberListNormalizer.php index 3bf93ec..1225898 100644 --- a/src/Serializer/SubscriberListNormalizer.php +++ b/src/Serializer/SubscriberListNormalizer.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Serializer; -use PhpList\Core\Domain\Model\Subscription\SubscriberList; +use PhpList\Core\Domain\Subscription\Model\SubscriberList; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class SubscriberListNormalizer implements NormalizerInterface diff --git a/src/Serializer/SubscriberNormalizer.php b/src/Serializer/SubscriberNormalizer.php index fb175b5..e72a51a 100644 --- a/src/Serializer/SubscriberNormalizer.php +++ b/src/Serializer/SubscriberNormalizer.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Serializer; -use PhpList\Core\Domain\Model\Subscription\Subscriber; -use PhpList\Core\Domain\Model\Subscription\Subscription; +use PhpList\Core\Domain\Subscription\Model\Subscriber; +use PhpList\Core\Domain\Subscription\Model\Subscription; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class SubscriberNormalizer implements NormalizerInterface diff --git a/src/Serializer/SubscriptionNormalizer.php b/src/Serializer/SubscriptionNormalizer.php index 58df4fb..4f8ec23 100644 --- a/src/Serializer/SubscriptionNormalizer.php +++ b/src/Serializer/SubscriptionNormalizer.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Serializer; -use PhpList\Core\Domain\Model\Subscription\Subscription; +use PhpList\Core\Domain\Subscription\Model\Subscription; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class SubscriptionNormalizer implements NormalizerInterface diff --git a/src/Serializer/TemplateImageNormalizer.php b/src/Serializer/TemplateImageNormalizer.php index 2a84bab..fd0888b 100644 --- a/src/Serializer/TemplateImageNormalizer.php +++ b/src/Serializer/TemplateImageNormalizer.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Serializer; -use PhpList\Core\Domain\Model\Messaging\TemplateImage; +use PhpList\Core\Domain\Messaging\Model\TemplateImage; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class TemplateImageNormalizer implements NormalizerInterface diff --git a/src/Serializer/TemplateNormalizer.php b/src/Serializer/TemplateNormalizer.php index bdf29e4..6cf988c 100644 --- a/src/Serializer/TemplateNormalizer.php +++ b/src/Serializer/TemplateNormalizer.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Serializer; -use PhpList\Core\Domain\Model\Messaging\Template; -use PhpList\Core\Domain\Model\Messaging\TemplateImage; +use PhpList\Core\Domain\Messaging\Model\Template; +use PhpList\Core\Domain\Messaging\Model\TemplateImage; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class TemplateNormalizer implements NormalizerInterface diff --git a/src/Service/Provider/PaginatedDataProvider.php b/src/Service/Provider/PaginatedDataProvider.php index 1c8fcef..fa6c72f 100644 --- a/src/Service/Provider/PaginatedDataProvider.php +++ b/src/Service/Provider/PaginatedDataProvider.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Service\Provider; use Doctrine\ORM\EntityManagerInterface; -use PhpList\Core\Domain\Model\Dto\Filter\FilterRequestInterface; -use PhpList\Core\Domain\Repository\Interfaces\PaginatableRepositoryInterface; +use PhpList\Core\Domain\Common\Model\Filter\FilterRequestInterface; +use PhpList\Core\Domain\Common\Repository\Interfaces\PaginatableRepositoryInterface; use PhpList\RestBundle\Entity\Dto\CursorPaginationResult; use PhpList\RestBundle\Serializer\CursorPaginationNormalizer; use PhpList\RestBundle\Service\Factory\PaginationCursorRequestFactory; diff --git a/src/Validator/Constraint/EmailExistsValidator.php b/src/Validator/Constraint/EmailExistsValidator.php index 9c4f5b5..f2cea3a 100644 --- a/src/Validator/Constraint/EmailExistsValidator.php +++ b/src/Validator/Constraint/EmailExistsValidator.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Validator\Constraint; -use PhpList\Core\Domain\Repository\Subscription\SubscriberRepository; +use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; diff --git a/src/Validator/Constraint/TemplateExistsValidator.php b/src/Validator/Constraint/TemplateExistsValidator.php index 0cfdc08..cf23949 100644 --- a/src/Validator/Constraint/TemplateExistsValidator.php +++ b/src/Validator/Constraint/TemplateExistsValidator.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Validator\Constraint; -use PhpList\Core\Domain\Repository\Messaging\TemplateRepository; +use PhpList\Core\Domain\Messaging\Repository\TemplateRepository; use Symfony\Component\HttpKernel\Exception\ConflictHttpException; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; diff --git a/src/Validator/Constraint/UniqueLoginNameValidator.php b/src/Validator/Constraint/UniqueLoginNameValidator.php index ded32c2..b0e0af7 100644 --- a/src/Validator/Constraint/UniqueLoginNameValidator.php +++ b/src/Validator/Constraint/UniqueLoginNameValidator.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Validator\Constraint; -use PhpList\Core\Domain\Repository\Identity\AdministratorRepository; +use PhpList\Core\Domain\Identity\Repository\AdministratorRepository; use Symfony\Component\HttpKernel\Exception\ConflictHttpException; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; diff --git a/tests/Helpers/DummyPaginatableRepository.php b/tests/Helpers/DummyPaginatableRepository.php index 3a55876..8f1e861 100644 --- a/tests/Helpers/DummyPaginatableRepository.php +++ b/tests/Helpers/DummyPaginatableRepository.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Tests\Helpers; use Doctrine\ORM\EntityRepository; -use PhpList\Core\Domain\Model\Dto\Filter\FilterRequestInterface; -use PhpList\Core\Domain\Repository\Interfaces\PaginatableRepositoryInterface; +use PhpList\Core\Domain\Common\Model\Filter\FilterRequestInterface; +use PhpList\Core\Domain\Common\Repository\Interfaces\PaginatableRepositoryInterface; class DummyPaginatableRepository extends EntityRepository implements PaginatableRepositoryInterface { diff --git a/tests/Integration/Controller/AttributeDefinitionControllerTest.php b/tests/Integration/Controller/AttributeDefinitionControllerTest.php index 807e1fc..e117119 100644 --- a/tests/Integration/Controller/AttributeDefinitionControllerTest.php +++ b/tests/Integration/Controller/AttributeDefinitionControllerTest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Tests\Integration\Controller; -use PhpList\Core\Domain\Repository\Subscription\SubscriberAttributeDefinitionRepository; +use PhpList\Core\Domain\Subscription\Repository\SubscriberAttributeDefinitionRepository; use PhpList\RestBundle\Controller\SubscriberAttributeDefinitionController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; diff --git a/tests/Integration/Controller/Fixtures/Identity/AdministratorFixture.php b/tests/Integration/Controller/Fixtures/Identity/AdministratorFixture.php index 282c79c..9eac66b 100644 --- a/tests/Integration/Controller/Fixtures/Identity/AdministratorFixture.php +++ b/tests/Integration/Controller/Fixtures/Identity/AdministratorFixture.php @@ -7,7 +7,7 @@ use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; -use PhpList\Core\Domain\Model\Identity\Administrator; +use PhpList\Core\Domain\Identity\Model\Administrator; use PhpList\Core\TestingSupport\Traits\ModelTestTrait; use RuntimeException; diff --git a/tests/Integration/Controller/Fixtures/Identity/AdministratorTokenFixture.php b/tests/Integration/Controller/Fixtures/Identity/AdministratorTokenFixture.php index b22998d..bac1160 100644 --- a/tests/Integration/Controller/Fixtures/Identity/AdministratorTokenFixture.php +++ b/tests/Integration/Controller/Fixtures/Identity/AdministratorTokenFixture.php @@ -7,8 +7,8 @@ use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; -use PhpList\Core\Domain\Model\Identity\Administrator; -use PhpList\Core\Domain\Model\Identity\AdministratorToken; +use PhpList\Core\Domain\Identity\Model\Administrator; +use PhpList\Core\Domain\Identity\Model\AdministratorToken; use PhpList\Core\TestingSupport\Traits\ModelTestTrait; use RuntimeException; diff --git a/tests/Integration/Controller/Fixtures/Messaging/MessageFixture.php b/tests/Integration/Controller/Fixtures/Messaging/MessageFixture.php index 0986b4f..7135aa2 100644 --- a/tests/Integration/Controller/Fixtures/Messaging/MessageFixture.php +++ b/tests/Integration/Controller/Fixtures/Messaging/MessageFixture.php @@ -7,14 +7,14 @@ use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; -use PhpList\Core\Domain\Model\Identity\Administrator; -use PhpList\Core\Domain\Model\Messaging\Message; -use PhpList\Core\Domain\Model\Messaging\Message\MessageContent; -use PhpList\Core\Domain\Model\Messaging\Message\MessageFormat; -use PhpList\Core\Domain\Model\Messaging\Message\MessageMetadata; -use PhpList\Core\Domain\Model\Messaging\Message\MessageOptions; -use PhpList\Core\Domain\Model\Messaging\Message\MessageSchedule; -use PhpList\Core\Domain\Model\Messaging\Template; +use PhpList\Core\Domain\Identity\Model\Administrator; +use PhpList\Core\Domain\Messaging\Model\Message; +use PhpList\Core\Domain\Messaging\Model\Message\MessageContent; +use PhpList\Core\Domain\Messaging\Model\Message\MessageFormat; +use PhpList\Core\Domain\Messaging\Model\Message\MessageMetadata; +use PhpList\Core\Domain\Messaging\Model\Message\MessageOptions; +use PhpList\Core\Domain\Messaging\Model\Message\MessageSchedule; +use PhpList\Core\Domain\Messaging\Model\Template; use PhpList\Core\TestingSupport\Traits\ModelTestTrait; use RuntimeException; diff --git a/tests/Integration/Controller/Fixtures/Messaging/SubscriberListFixture.php b/tests/Integration/Controller/Fixtures/Messaging/SubscriberListFixture.php index 4d743f1..11ea246 100644 --- a/tests/Integration/Controller/Fixtures/Messaging/SubscriberListFixture.php +++ b/tests/Integration/Controller/Fixtures/Messaging/SubscriberListFixture.php @@ -7,8 +7,8 @@ use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; -use PhpList\Core\Domain\Model\Identity\Administrator; -use PhpList\Core\Domain\Model\Subscription\SubscriberList; +use PhpList\Core\Domain\Identity\Model\Administrator; +use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\Core\TestingSupport\Traits\ModelTestTrait; use RuntimeException; diff --git a/tests/Integration/Controller/Fixtures/Messaging/TemplateFixture.php b/tests/Integration/Controller/Fixtures/Messaging/TemplateFixture.php index 8daf6e5..5042f3c 100644 --- a/tests/Integration/Controller/Fixtures/Messaging/TemplateFixture.php +++ b/tests/Integration/Controller/Fixtures/Messaging/TemplateFixture.php @@ -1,10 +1,12 @@ 1, ]); - $content = new MessageContent('Subject', 'Text', 'TextMsg', 'Footer'); - $format = new MessageFormat(true, 'html'); + $content = new Message\MessageContent('Subject', 'Text', 'TextMsg', 'Footer'); + $format = new Message\MessageFormat(true, 'html'); $format->setFormatOptions(['text', 'html']); $entered = new DateTime('2025-01-01T10:00:00+00:00'); $sent = new DateTime('2025-01-02T10:00:00+00:00'); - $metadata = new MessageMetadata('draft'); + $metadata = new Message\MessageMetadata('draft'); $metadata->setProcessed(true); $metadata->setViews(10); $metadata->setBounceCount(3); $metadata->setEntered($entered); $metadata->setSent($sent); - $schedule = new MessageSchedule( + $schedule = new Message\MessageSchedule( 24, new DateTime('2025-01-10T00:00:00+00:00'), 12, @@ -66,7 +61,7 @@ public function testNormalizeReturnsExpectedArray(): void new DateTime('2025-01-01T00:00:00+00:00') ); - $options = new MessageOptions('from@example.com', 'to@example.com', 'reply@example.com', 'group'); + $options = new Message\MessageOptions('from@example.com', 'to@example.com', 'reply@example.com', 'group'); $message = $this->createMock(Message::class); $message->method('getId')->willReturn(1); diff --git a/tests/Unit/Serializer/SubscriberListNormalizerTest.php b/tests/Unit/Serializer/SubscriberListNormalizerTest.php index aafde29..d24fdd7 100644 --- a/tests/Unit/Serializer/SubscriberListNormalizerTest.php +++ b/tests/Unit/Serializer/SubscriberListNormalizerTest.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; use DateTime; -use PhpList\Core\Domain\Model\Subscription\SubscriberList; +use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\RestBundle\Serializer\SubscriberListNormalizer; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Serializer/SubscriberNormalizerTest.php b/tests/Unit/Serializer/SubscriberNormalizerTest.php index b0d6168..9960fac 100644 --- a/tests/Unit/Serializer/SubscriberNormalizerTest.php +++ b/tests/Unit/Serializer/SubscriberNormalizerTest.php @@ -5,9 +5,9 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; use DateTime; -use PhpList\Core\Domain\Model\Subscription\Subscriber; -use PhpList\Core\Domain\Model\Subscription\SubscriberList; -use PhpList\Core\Domain\Model\Subscription\Subscription; +use PhpList\Core\Domain\Subscription\Model\Subscriber; +use PhpList\Core\Domain\Subscription\Model\SubscriberList; +use PhpList\Core\Domain\Subscription\Model\Subscription; use PhpList\RestBundle\Serializer\SubscriberNormalizer; use PHPUnit\Framework\TestCase; use Doctrine\Common\Collections\ArrayCollection; diff --git a/tests/Unit/Serializer/SubscriptionNormalizerTest.php b/tests/Unit/Serializer/SubscriptionNormalizerTest.php index b7de91f..c192317 100644 --- a/tests/Unit/Serializer/SubscriptionNormalizerTest.php +++ b/tests/Unit/Serializer/SubscriptionNormalizerTest.php @@ -5,9 +5,9 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; use DateTime; -use PhpList\Core\Domain\Model\Subscription\Subscriber; -use PhpList\Core\Domain\Model\Subscription\SubscriberList; -use PhpList\Core\Domain\Model\Subscription\Subscription; +use PhpList\Core\Domain\Subscription\Model\Subscriber; +use PhpList\Core\Domain\Subscription\Model\SubscriberList; +use PhpList\Core\Domain\Subscription\Model\Subscription; use PhpList\RestBundle\Serializer\SubscriberListNormalizer; use PhpList\RestBundle\Serializer\SubscriberNormalizer; use PhpList\RestBundle\Serializer\SubscriptionNormalizer; diff --git a/tests/Unit/Serializer/TemplateImageNormalizerTest.php b/tests/Unit/Serializer/TemplateImageNormalizerTest.php index 3a3a7dd..23f59d4 100644 --- a/tests/Unit/Serializer/TemplateImageNormalizerTest.php +++ b/tests/Unit/Serializer/TemplateImageNormalizerTest.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; -use PhpList\Core\Domain\Model\Messaging\Template; -use PhpList\Core\Domain\Model\Messaging\TemplateImage; +use PhpList\Core\Domain\Messaging\Model\Template; +use PhpList\Core\Domain\Messaging\Model\TemplateImage; use PhpList\RestBundle\Serializer\TemplateImageNormalizer; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Serializer/TemplateNormalizerTest.php b/tests/Unit/Serializer/TemplateNormalizerTest.php index 23bac70..4b5ea76 100644 --- a/tests/Unit/Serializer/TemplateNormalizerTest.php +++ b/tests/Unit/Serializer/TemplateNormalizerTest.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; -use PhpList\Core\Domain\Model\Messaging\Template; -use PhpList\Core\Domain\Model\Messaging\TemplateImage; +use PhpList\Core\Domain\Messaging\Model\Template; +use PhpList\Core\Domain\Messaging\Model\TemplateImage; use PhpList\RestBundle\Serializer\TemplateImageNormalizer; use PhpList\RestBundle\Serializer\TemplateNormalizer; use PHPUnit\Framework\MockObject\MockObject; diff --git a/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php b/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php index c989b79..6b454c9 100644 --- a/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php +++ b/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Tests\Unit\Validator\Constraint; -use PhpList\Core\Domain\Model\Subscription\Subscriber; -use PhpList\Core\Domain\Repository\Subscription\SubscriberRepository; +use PhpList\Core\Domain\Subscription\Model\Subscriber; +use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository; use PhpList\RestBundle\Validator\Constraint\EmailExists; use PhpList\RestBundle\Validator\Constraint\EmailExistsValidator; use PHPUnit\Framework\MockObject\MockObject; diff --git a/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php b/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php index 04e478d..ff17a6b 100644 --- a/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php +++ b/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Tests\Unit\Validator\Constraint; -use PhpList\Core\Domain\Model\Messaging\Template; -use PhpList\Core\Domain\Repository\Messaging\TemplateRepository; +use PhpList\Core\Domain\Messaging\Model\Template; +use PhpList\Core\Domain\Messaging\Repository\TemplateRepository; use PhpList\RestBundle\Validator\Constraint\TemplateExists; use PhpList\RestBundle\Validator\Constraint\TemplateExistsValidator; use PHPUnit\Framework\MockObject\MockObject; diff --git a/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php b/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php index 4645c0e..ffa7848 100644 --- a/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php +++ b/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php @@ -5,8 +5,8 @@ namespace PhpList\RestBundle\Tests\Unit\Validator\Constraint; use Doctrine\ORM\EntityManagerInterface; -use PhpList\Core\Domain\Model\Subscription\Subscriber; -use PhpList\Core\Domain\Repository\Subscription\SubscriberRepository; +use PhpList\Core\Domain\Subscription\Model\Subscriber; +use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository; use PhpList\RestBundle\Validator\Constraint\UniqueEmail; use PhpList\RestBundle\Validator\Constraint\UniqueEmailValidator; use PHPUnit\Framework\MockObject\MockObject; diff --git a/tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php b/tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php index af86b85..90a75f7 100644 --- a/tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php +++ b/tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Tests\Unit\Validator\Constraint; -use PhpList\Core\Domain\Model\Identity\Administrator; -use PhpList\Core\Domain\Repository\Identity\AdministratorRepository; +use PhpList\Core\Domain\Identity\Model\Administrator; +use PhpList\Core\Domain\Identity\Repository\AdministratorRepository; use PhpList\RestBundle\Validator\Constraint\UniqueLoginName; use PhpList\RestBundle\Validator\Constraint\UniqueLoginNameValidator; use PHPUnit\Framework\TestCase; From 425a35b2a0eac462ff75cc5ac04e15493d62a10d Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 11 May 2025 20:51:55 +0400 Subject: [PATCH 06/21] ISSUE-345: Feature-based structure --- composer.json | 14 ++- config/services.yml | 6 -- config/services/controllers.yml | 26 ++++++ config/services/factories.yml | 2 +- config/services/listeners.yml | 4 +- config/services/normalizers.yml | 22 ++--- config/services/providers.yml | 2 +- config/services/validators.yml | 17 ++-- .../Controller/BaseController.php | 4 +- .../Dto/CursorPaginationResult.php | 2 +- .../Dto/ValidationContext.php | 2 +- .../EventListener/ExceptionListener.php | 2 +- .../EventListener/ResponseListener.php | 2 +- .../Request/PaginationCursorRequest.php | 2 +- .../Request/RequestInterface.php | 2 +- .../Serializer/CursorPaginationNormalizer.php | 4 +- .../PaginationCursorRequestFactory.php | 4 +- .../Provider/PaginatedDataProvider.php | 8 +- .../SwaggerSchemasResponse.php | 2 +- .../Validator/RequestValidator.php | 4 +- .../Controller/AdministratorController.php | 13 +-- .../Controller/SessionController.php | 9 +- .../OpenApi/SwaggerSchemasRequest.php | 76 ++++++++++++++++ .../OpenApi/SwaggerSchemasResponse.php | 27 ++++++ .../Request/CreateAdministratorRequest.php | 10 ++- .../Request/CreateSessionRequest.php | 3 +- .../Request/UpdateAdministratorRequest.php | 10 ++- .../Serializer/AdministratorNormalizer.php | 2 +- .../AdministratorTokenNormalizer.php | 2 +- .../Validator/Constraint/UniqueEmail.php | 2 +- .../Constraint/UniqueEmailValidator.php | 4 +- .../Validator/Constraint/UniqueLoginName.php | 2 +- .../Constraint/UniqueLoginNameValidator.php | 2 +- .../Controller/CampaignController.php | 13 +-- .../Controller/TemplateController.php | 11 +-- .../OpenApi/SwaggerSchemasRequest.php} | 69 +-------------- .../OpenApi/SwaggerSchemasResponse.php} | 88 +------------------ .../Request/CreateMessageRequest.php | 17 ++-- .../Request/CreateTemplateRequest.php | 8 +- .../Request/Message/MessageContentRequest.php | 2 +- .../Request/Message/MessageFormatRequest.php | 2 +- .../Message/MessageMetadataRequest.php | 2 +- .../Request/Message/MessageOptionsRequest.php | 2 +- .../Message/MessageScheduleRequest.php | 2 +- .../Request/Message/RequestDtoInterface.php | 2 +- .../Request/UpdateMessageRequest.php | 2 +- .../Serializer/MessageNormalizer.php | 2 +- .../Serializer/TemplateImageNormalizer.php | 2 +- .../Serializer/TemplateNormalizer.php | 2 +- .../Constraint/ContainsPlaceholder.php | 2 +- .../ContainsPlaceholderValidator.php | 2 +- .../Validator/Constraint/TemplateExists.php | 2 +- .../Constraint/TemplateExistsValidator.php | 2 +- .../Controller/ListController.php | 21 ++--- .../Controller/ListMembersController.php | 9 +- ...ubscriberAttributeDefinitionController.php | 13 +-- .../Controller/SubscriberController.php | 11 +-- .../Controller/SubscriptionController.php | 9 +- .../OpenApi/SwaggerSchemasRequest.php | 22 +++++ .../OpenApi/SwaggerSchemasResponse.php | 79 +++++++++++++++++ .../CreateAttributeDefinitionRequest.php | 3 +- .../Request/CreateSubscriberListRequest.php | 3 +- .../Request/CreateSubscriberRequest.php | 7 +- .../Request/SubscriptionRequest.php | 5 +- .../UpdateAttributeDefinitionRequest.php | 3 +- .../Request/UpdateSubscriberRequest.php | 7 +- .../AttributeDefinitionNormalizer.php | 2 +- .../SubscriberAttributeValueNormalizer.php | 2 +- .../Serializer/SubscriberListNormalizer.php | 2 +- .../Serializer/SubscriberNormalizer.php | 2 +- .../Serializer/SubscriptionNormalizer.php | 2 +- .../Validator/Constraint/EmailExists.php | 2 +- .../Constraint/EmailExistsValidator.php | 2 +- .../Validator/Constraint/UniqueEmail.php | 25 ++++++ .../Constraint/UniqueEmailValidator.php | 45 ++++++++++ tests/Helpers/DummyRequestDto.php | 2 +- tests/Integration/Composer/ScriptsTest.php | 5 +- .../AttributeDefinitionControllerTest.php | 2 +- .../Controller/CampaignControllerTest.php | 2 +- .../Controller/ListControllerTest.php | 2 +- .../Controller/ListMembersControllerTest.php | 2 +- .../Controller/SessionControllerTest.php | 2 +- .../Controller/SubscriberControllerTest.php | 2 +- .../Controller/SubscriptionControllerTest.php | 2 +- .../Controller/TemplateControllerTest.php | 2 +- .../EventListener/ExceptionListenerTest.php | 6 +- .../EventListener/ResponseListenerTest.php | 2 +- .../AdministratorNormalizerTest.php | 2 +- .../AdministratorTokenNormalizerTest.php | 2 +- .../AttributeDefinitionNormalizerTest.php | 2 +- .../CursorPaginationNormalizerTest.php | 4 +- .../Unit/Serializer/MessageNormalizerTest.php | 6 +- .../SubscriberListNormalizerTest.php | 2 +- .../Serializer/SubscriberNormalizerTest.php | 4 +- .../Serializer/SubscriptionNormalizerTest.php | 6 +- .../TemplateImageNormalizerTest.php | 2 +- .../Serializer/TemplateNormalizerTest.php | 6 +- .../PaginationCursorRequestFactoryTest.php | 2 +- .../Provider/PaginatedDataProviderTest.php | 12 +-- .../ContainsPlaceholderValidatorTest.php | 4 +- .../Constraint/EmailExistsValidatorTest.php | 4 +- .../TemplateExistsValidatorTest.php | 4 +- .../Constraint/UniqueEmailValidatorTest.php | 4 +- .../UniqueLoginNameValidatorTest.php | 6 +- tests/Unit/Validator/RequestValidatorTest.php | 4 +- 105 files changed, 543 insertions(+), 367 deletions(-) create mode 100644 config/services/controllers.yml rename src/{ => Common}/Controller/BaseController.php (91%) rename src/{Entity => Common}/Dto/CursorPaginationResult.php (84%) rename src/{Entity => Common}/Dto/ValidationContext.php (91%) rename src/{ => Common}/EventListener/ExceptionListener.php (96%) rename src/{ => Common}/EventListener/ResponseListener.php (91%) rename src/{Entity => Common}/Request/PaginationCursorRequest.php (92%) rename src/{Entity => Common}/Request/RequestInterface.php (69%) rename src/{ => Common}/Serializer/CursorPaginationNormalizer.php (91%) rename src/{ => Common}/Service/Factory/PaginationCursorRequestFactory.php (75%) rename src/{ => Common}/Service/Provider/PaginatedDataProvider.php (85%) rename src/{OpenApi => Common}/SwaggerSchemasResponse.php (97%) rename src/{ => Common}/Validator/RequestValidator.php (95%) rename src/{ => Identity}/Controller/AdministratorController.php (95%) rename src/{ => Identity}/Controller/SessionController.php (94%) create mode 100644 src/Identity/OpenApi/SwaggerSchemasRequest.php create mode 100644 src/Identity/OpenApi/SwaggerSchemasResponse.php rename src/{Entity => Identity}/Request/CreateAdministratorRequest.php (73%) rename src/{Entity => Identity}/Request/CreateSessionRequest.php (79%) rename src/{Entity => Identity}/Request/UpdateAdministratorRequest.php (75%) rename src/{ => Identity}/Serializer/AdministratorNormalizer.php (95%) rename src/{ => Identity}/Serializer/AdministratorTokenNormalizer.php (94%) rename src/{ => Identity}/Validator/Constraint/UniqueEmail.php (90%) rename src/{ => Identity}/Validator/Constraint/UniqueEmailValidator.php (91%) rename src/{ => Identity}/Validator/Constraint/UniqueLoginName.php (91%) rename src/{ => Identity}/Validator/Constraint/UniqueLoginNameValidator.php (96%) rename src/{ => Messaging}/Controller/CampaignController.php (96%) rename src/{ => Messaging}/Controller/TemplateController.php (96%) rename src/{OpenApi/SwaggerSchemasRequestDto.php => Messaging/OpenApi/SwaggerSchemasRequest.php} (61%) rename src/{OpenApi/SwaggerSchemasResponseEntity.php => Messaging/OpenApi/SwaggerSchemasResponse.php} (60%) rename src/{Entity => Messaging}/Request/CreateMessageRequest.php (66%) rename src/{Entity => Messaging}/Request/CreateTemplateRequest.php (80%) rename src/{Entity => Messaging}/Request/Message/MessageContentRequest.php (92%) rename src/{Entity => Messaging}/Request/Message/MessageFormatRequest.php (93%) rename src/{Entity => Messaging}/Request/Message/MessageMetadataRequest.php (88%) rename src/{Entity => Messaging}/Request/Message/MessageOptionsRequest.php (92%) rename src/{Entity => Messaging}/Request/Message/MessageScheduleRequest.php (93%) rename src/{Entity => Messaging}/Request/Message/RequestDtoInterface.php (55%) rename src/{Entity => Messaging}/Request/UpdateMessageRequest.php (93%) rename src/{ => Messaging}/Serializer/MessageNormalizer.php (98%) rename src/{ => Messaging}/Serializer/TemplateImageNormalizer.php (95%) rename src/{ => Messaging}/Serializer/TemplateNormalizer.php (96%) rename src/{ => Messaging}/Validator/Constraint/ContainsPlaceholder.php (86%) rename src/{ => Messaging}/Validator/Constraint/ContainsPlaceholderValidator.php (93%) rename src/{ => Messaging}/Validator/Constraint/TemplateExists.php (91%) rename src/{ => Messaging}/Validator/Constraint/TemplateExistsValidator.php (95%) rename src/{ => Subscription}/Controller/ListController.php (92%) rename src/{ => Subscription}/Controller/ListMembersController.php (95%) rename src/{ => Subscription}/Controller/SubscriberAttributeDefinitionController.php (96%) rename src/{ => Subscription}/Controller/SubscriberController.php (96%) rename src/{ => Subscription}/Controller/SubscriptionController.php (95%) create mode 100644 src/Subscription/OpenApi/SwaggerSchemasRequest.php create mode 100644 src/Subscription/OpenApi/SwaggerSchemasResponse.php rename src/{Entity => Subscription}/Request/CreateAttributeDefinitionRequest.php (87%) rename src/{Entity => Subscription}/Request/CreateSubscriberListRequest.php (86%) rename src/{Entity => Subscription}/Request/CreateSubscriberRequest.php (77%) rename src/{Entity => Subscription}/Request/SubscriptionRequest.php (70%) rename src/{Entity => Subscription}/Request/UpdateAttributeDefinitionRequest.php (87%) rename src/{Entity => Subscription}/Request/UpdateSubscriberRequest.php (83%) rename src/{ => Subscription}/Serializer/AttributeDefinitionNormalizer.php (95%) rename src/{ => Subscription}/Serializer/SubscriberAttributeValueNormalizer.php (95%) rename src/{ => Subscription}/Serializer/SubscriberListNormalizer.php (95%) rename src/{ => Subscription}/Serializer/SubscriberNormalizer.php (97%) rename src/{ => Subscription}/Serializer/SubscriptionNormalizer.php (96%) rename src/{ => Subscription}/Validator/Constraint/EmailExists.php (90%) rename src/{ => Subscription}/Validator/Constraint/EmailExistsValidator.php (95%) create mode 100644 src/Subscription/Validator/Constraint/UniqueEmail.php create mode 100644 src/Subscription/Validator/Constraint/UniqueEmailValidator.php diff --git a/composer.json b/composer.json index 06c8ac9..5f8833c 100644 --- a/composer.json +++ b/composer.json @@ -97,8 +97,18 @@ "PhpList\\RestBundle\\PhpListRestBundle" ], "routes": { - "rest-api": { - "resource": "@PhpListRestBundle/Controller/", + "rest-api-identity": { + "resource": "@PhpListRestBundle/Identity/Controller/", + "type": "attribute", + "prefix": "/api/v2" + }, + "rest-api-subscription": { + "resource": "@PhpListRestBundle/Subscription/Controller/", + "type": "attribute", + "prefix": "/api/v2" + }, + "rest-api-messaging": { + "resource": "@PhpListRestBundle/Messaging/Controller/", "type": "attribute", "prefix": "/api/v2" } diff --git a/config/services.yml b/config/services.yml index 4d668a3..086d199 100644 --- a/config/services.yml +++ b/config/services.yml @@ -4,12 +4,6 @@ services: Psr\Container\ContainerInterface: alias: 'service_container' - PhpList\RestBundle\Controller\: - resource: '../src/Controller' - public: true - autowire: true - tags: ['controller.service_arguments'] - my.secure_handler: class: PhpList\RestBundle\ViewHandler\SecuredViewHandler diff --git a/config/services/controllers.yml b/config/services/controllers.yml new file mode 100644 index 0000000..857146f --- /dev/null +++ b/config/services/controllers.yml @@ -0,0 +1,26 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + PhpList\RestBundle\Identity\Controller\: + resource: '../src/Identity/Controller' + tags: [ 'controller.service_arguments' ] + autowire: true + autoconfigure: true + public: true + + PhpList\RestBundle\Messaging\Controller\: + resource: '../src/Messaging/Controller' + tags: [ 'controller.service_arguments' ] + autowire: true + autoconfigure: true + public: true + + PhpList\RestBundle\Subscription\Controller\: + resource: '../src/Subscription/Controller' + tags: [ 'controller.service_arguments' ] + autowire: true + autoconfigure: true + public: true diff --git a/config/services/factories.yml b/config/services/factories.yml index 3a8734d..459a86f 100644 --- a/config/services/factories.yml +++ b/config/services/factories.yml @@ -4,6 +4,6 @@ services: autoconfigure: true public: false - PhpList\RestBundle\Service\Factory\PaginationCursorRequestFactory: + PhpList\RestBundle\Common\Service\Factory\PaginationCursorRequestFactory: autowire: true autoconfigure: true diff --git a/config/services/listeners.yml b/config/services/listeners.yml index 6257282..be859c9 100644 --- a/config/services/listeners.yml +++ b/config/services/listeners.yml @@ -4,10 +4,10 @@ services: autoconfigure: true public: false - PhpList\RestBundle\EventListener\ExceptionListener: + PhpList\RestBundle\Common\EventListener\ExceptionListener: tags: - { name: kernel.event_listener, event: kernel.exception } - PhpList\RestBundle\EventListener\ResponseListener: + PhpList\RestBundle\Common\EventListener\ResponseListener: tags: - { name: kernel.event_listener, event: kernel.response } diff --git a/config/services/normalizers.yml b/config/services/normalizers.yml index d2849df..f2c3e43 100644 --- a/config/services/normalizers.yml +++ b/config/services/normalizers.yml @@ -11,45 +11,45 @@ services: $classMetadataFactory: '@?serializer.mapping.class_metadata_factory' $nameConverter: '@Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter' - PhpList\RestBundle\Serializer\SubscriberNormalizer: + PhpList\RestBundle\Subscription\Serializer\SubscriberNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\AdministratorTokenNormalizer: + PhpList\RestBundle\Identity\Serializer\AdministratorTokenNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\SubscriberListNormalizer: + PhpList\RestBundle\Subscription\Serializer\SubscriberListNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\SubscriptionNormalizer: + PhpList\RestBundle\Subscription\Serializer\SubscriptionNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\MessageNormalizer: + PhpList\RestBundle\Messaging\Serializer\MessageNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\TemplateImageNormalizer: + PhpList\RestBundle\Messaging\Serializer\TemplateImageNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\TemplateNormalizer: + PhpList\RestBundle\Messaging\Serializer\TemplateNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\AdministratorNormalizer: + PhpList\RestBundle\Identity\Serializer\AdministratorNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\AttributeDefinitionNormalizer: + PhpList\RestBundle\Subscription\Serializer\AttributeDefinitionNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\SubscriberAttributeValueNormalizer: + PhpList\RestBundle\Subscription\Serializer\SubscriberAttributeValueNormalizer: tags: [ 'serializer.normalizer' ] autowire: true - PhpList\RestBundle\Serializer\CursorPaginationNormalizer: + PhpList\RestBundle\Common\Serializer\CursorPaginationNormalizer: autowire: true diff --git a/config/services/providers.yml b/config/services/providers.yml index 49c7ff7..be69707 100644 --- a/config/services/providers.yml +++ b/config/services/providers.yml @@ -4,6 +4,6 @@ services: autoconfigure: true public: false - PhpList\RestBundle\Service\Provider\PaginatedDataProvider: + PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider: autowire: true autoconfigure: true diff --git a/config/services/validators.yml b/config/services/validators.yml index 2579b6a..f94c47f 100644 --- a/config/services/validators.yml +++ b/config/services/validators.yml @@ -1,20 +1,25 @@ services: - PhpList\RestBundle\Validator\RequestValidator: + PhpList\RestBundle\Common\Validator\RequestValidator: autowire: true autoconfigure: true public: false - PhpList\RestBundle\Validator\Constraint\UniqueEmailValidator: + PhpList\RestBundle\Identity\Validator\UniqueEmailValidator: autowire: true autoconfigure: true tags: [ 'validator.constraint_validator' ] - PhpList\RestBundle\Validator\Constraint\EmailExistsValidator: + PhpList\RestBundle\Subscription\Validator\Constraint\UniqueEmailValidator: autowire: true autoconfigure: true tags: [ 'validator.constraint_validator' ] - PhpList\RestBundle\Validator\Constraint\TemplateExistsValidator: + PhpList\RestBundle\Subscription\Validator\Constraint\EmailExistsValidator: + autowire: true + autoconfigure: true + tags: [ 'validator.constraint_validator' ] + + PhpList\RestBundle\Messaging\Validator\Constraint\TemplateExistsValidator: autowire: true autoconfigure: true tags: [ 'validator.constraint_validator' ] @@ -27,10 +32,10 @@ services: autowire: true autoconfigure: true - PhpList\RestBundle\Validator\Constraint\ContainsPlaceholderValidator: + PhpList\RestBundle\Messaging\Validator\Constraint\ContainsPlaceholderValidator: tags: ['validator.constraint_validator'] - PhpList\RestBundle\Validator\Constraint\UniqueLoginNameValidator: + PhpList\RestBundle\Identity\Validator\Constraint\UniqueLoginNameValidator: autowire: true autoconfigure: true tags: [ 'validator.constraint_validator' ] diff --git a/src/Controller/BaseController.php b/src/Common/Controller/BaseController.php similarity index 91% rename from src/Controller/BaseController.php rename to src/Common/Controller/BaseController.php index 5e0a950..32bed0a 100644 --- a/src/Controller/BaseController.php +++ b/src/Common/Controller/BaseController.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Common\Controller; use PhpList\Core\Domain\Identity\Model\Administrator; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Validator\RequestValidator; +use PhpList\RestBundle\Common\Validator\RequestValidator; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; diff --git a/src/Entity/Dto/CursorPaginationResult.php b/src/Common/Dto/CursorPaginationResult.php similarity index 84% rename from src/Entity/Dto/CursorPaginationResult.php rename to src/Common/Dto/CursorPaginationResult.php index 0302f28..04fca6d 100644 --- a/src/Entity/Dto/CursorPaginationResult.php +++ b/src/Common/Dto/CursorPaginationResult.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Dto; +namespace PhpList\RestBundle\Common\Dto; class CursorPaginationResult { diff --git a/src/Entity/Dto/ValidationContext.php b/src/Common/Dto/ValidationContext.php similarity index 91% rename from src/Entity/Dto/ValidationContext.php rename to src/Common/Dto/ValidationContext.php index 225e0d9..162dd1c 100644 --- a/src/Entity/Dto/ValidationContext.php +++ b/src/Common/Dto/ValidationContext.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Dto; +namespace PhpList\RestBundle\Common\Dto; class ValidationContext { diff --git a/src/EventListener/ExceptionListener.php b/src/Common/EventListener/ExceptionListener.php similarity index 96% rename from src/EventListener/ExceptionListener.php rename to src/Common/EventListener/ExceptionListener.php index 35b6ec2..f48bf86 100644 --- a/src/EventListener/ExceptionListener.php +++ b/src/Common/EventListener/ExceptionListener.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\EventListener; +namespace PhpList\RestBundle\Common\EventListener; use Exception; use PhpList\Core\Domain\Subscription\Exception\SubscriptionCreationException; diff --git a/src/EventListener/ResponseListener.php b/src/Common/EventListener/ResponseListener.php similarity index 91% rename from src/EventListener/ResponseListener.php rename to src/Common/EventListener/ResponseListener.php index 2bfb915..b960ebd 100644 --- a/src/EventListener/ResponseListener.php +++ b/src/Common/EventListener/ResponseListener.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\EventListener; +namespace PhpList\RestBundle\Common\EventListener; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Event\ResponseEvent; diff --git a/src/Entity/Request/PaginationCursorRequest.php b/src/Common/Request/PaginationCursorRequest.php similarity index 92% rename from src/Entity/Request/PaginationCursorRequest.php rename to src/Common/Request/PaginationCursorRequest.php index bf82b4e..ef514e1 100644 --- a/src/Entity/Request/PaginationCursorRequest.php +++ b/src/Common/Request/PaginationCursorRequest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request; +namespace PhpList\RestBundle\Common\Request; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Entity/Request/RequestInterface.php b/src/Common/Request/RequestInterface.php similarity index 69% rename from src/Entity/Request/RequestInterface.php rename to src/Common/Request/RequestInterface.php index d87ad38..ce20c02 100644 --- a/src/Entity/Request/RequestInterface.php +++ b/src/Common/Request/RequestInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request; +namespace PhpList\RestBundle\Common\Request; interface RequestInterface { diff --git a/src/Serializer/CursorPaginationNormalizer.php b/src/Common/Serializer/CursorPaginationNormalizer.php similarity index 91% rename from src/Serializer/CursorPaginationNormalizer.php rename to src/Common/Serializer/CursorPaginationNormalizer.php index d8fa512..cefd084 100644 --- a/src/Serializer/CursorPaginationNormalizer.php +++ b/src/Common/Serializer/CursorPaginationNormalizer.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Serializer; +namespace PhpList\RestBundle\Common\Serializer; -use PhpList\RestBundle\Entity\Dto\CursorPaginationResult; +use PhpList\RestBundle\Common\Dto\CursorPaginationResult; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; class CursorPaginationNormalizer implements NormalizerInterface diff --git a/src/Service/Factory/PaginationCursorRequestFactory.php b/src/Common/Service/Factory/PaginationCursorRequestFactory.php similarity index 75% rename from src/Service/Factory/PaginationCursorRequestFactory.php rename to src/Common/Service/Factory/PaginationCursorRequestFactory.php index 28b561e..efa763a 100644 --- a/src/Service/Factory/PaginationCursorRequestFactory.php +++ b/src/Common/Service/Factory/PaginationCursorRequestFactory.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Service\Factory; +namespace PhpList\RestBundle\Common\Service\Factory; -use PhpList\RestBundle\Entity\Request\PaginationCursorRequest; +use PhpList\RestBundle\Common\Request\PaginationCursorRequest; use Symfony\Component\HttpFoundation\Request; class PaginationCursorRequestFactory diff --git a/src/Service/Provider/PaginatedDataProvider.php b/src/Common/Service/Provider/PaginatedDataProvider.php similarity index 85% rename from src/Service/Provider/PaginatedDataProvider.php rename to src/Common/Service/Provider/PaginatedDataProvider.php index fa6c72f..c5bf3a3 100644 --- a/src/Service/Provider/PaginatedDataProvider.php +++ b/src/Common/Service/Provider/PaginatedDataProvider.php @@ -2,14 +2,14 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Service\Provider; +namespace PhpList\RestBundle\Common\Service\Provider; use Doctrine\ORM\EntityManagerInterface; use PhpList\Core\Domain\Common\Model\Filter\FilterRequestInterface; use PhpList\Core\Domain\Common\Repository\Interfaces\PaginatableRepositoryInterface; -use PhpList\RestBundle\Entity\Dto\CursorPaginationResult; -use PhpList\RestBundle\Serializer\CursorPaginationNormalizer; -use PhpList\RestBundle\Service\Factory\PaginationCursorRequestFactory; +use PhpList\RestBundle\Common\Dto\CursorPaginationResult; +use PhpList\RestBundle\Common\Serializer\CursorPaginationNormalizer; +use PhpList\RestBundle\Common\Service\Factory\PaginationCursorRequestFactory; use RuntimeException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; diff --git a/src/OpenApi/SwaggerSchemasResponse.php b/src/Common/SwaggerSchemasResponse.php similarity index 97% rename from src/OpenApi/SwaggerSchemasResponse.php rename to src/Common/SwaggerSchemasResponse.php index efe9c5d..c50683c 100644 --- a/src/OpenApi/SwaggerSchemasResponse.php +++ b/src/Common/SwaggerSchemasResponse.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\OpenApi; +namespace PhpList\RestBundle\Common; use OpenApi\Attributes as OA; diff --git a/src/Validator/RequestValidator.php b/src/Common/Validator/RequestValidator.php similarity index 95% rename from src/Validator/RequestValidator.php rename to src/Common/Validator/RequestValidator.php index ae5a01d..df0ae67 100644 --- a/src/Validator/RequestValidator.php +++ b/src/Common/Validator/RequestValidator.php @@ -2,9 +2,9 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Validator; +namespace PhpList\RestBundle\Common\Validator; -use PhpList\RestBundle\Entity\Request\RequestInterface; +use PhpList\RestBundle\Common\Request\RequestInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException; diff --git a/src/Controller/AdministratorController.php b/src/Identity/Controller/AdministratorController.php similarity index 95% rename from src/Controller/AdministratorController.php rename to src/Identity/Controller/AdministratorController.php index 881e59e..9afedd5 100644 --- a/src/Controller/AdministratorController.php +++ b/src/Identity/Controller/AdministratorController.php @@ -2,17 +2,18 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Identity\Controller; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Identity\Model\Administrator; use PhpList\Core\Domain\Identity\Service\AdministratorManager; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Entity\Request\CreateAdministratorRequest; -use PhpList\RestBundle\Entity\Request\UpdateAdministratorRequest; -use PhpList\RestBundle\Serializer\AdministratorNormalizer; -use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; -use PhpList\RestBundle\Validator\RequestValidator; +use PhpList\RestBundle\Common\Controller\BaseController; +use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider; +use PhpList\RestBundle\Common\Validator\RequestValidator; +use PhpList\RestBundle\Identity\Request\CreateAdministratorRequest; +use PhpList\RestBundle\Identity\Request\UpdateAdministratorRequest; +use PhpList\RestBundle\Identity\Serializer\AdministratorNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Controller/SessionController.php b/src/Identity/Controller/SessionController.php similarity index 94% rename from src/Controller/SessionController.php rename to src/Identity/Controller/SessionController.php index 83cc05f..6b55676 100644 --- a/src/Controller/SessionController.php +++ b/src/Identity/Controller/SessionController.php @@ -2,15 +2,16 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Identity\Controller; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Identity\Model\AdministratorToken; use PhpList\Core\Domain\Identity\Service\SessionManager; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Entity\Request\CreateSessionRequest; -use PhpList\RestBundle\Serializer\AdministratorTokenNormalizer; -use PhpList\RestBundle\Validator\RequestValidator; +use PhpList\RestBundle\Common\Controller\BaseController; +use PhpList\RestBundle\Common\Validator\RequestValidator; +use PhpList\RestBundle\Identity\Request\CreateSessionRequest; +use PhpList\RestBundle\Identity\Serializer\AdministratorTokenNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Identity/OpenApi/SwaggerSchemasRequest.php b/src/Identity/OpenApi/SwaggerSchemasRequest.php new file mode 100644 index 0000000..3493a47 --- /dev/null +++ b/src/Identity/OpenApi/SwaggerSchemasRequest.php @@ -0,0 +1,76 @@ +findOneBy(['email' => $value]); $dto = $this->context->getObject(); - $updatingId = $dto->subscriberId ?? $dto->administratorId ?? null; + $updatingId = $dto->administratorId ?? null; if ($existingUser && $existingUser->getId() !== $updatingId) { throw new ConflictHttpException('Email already exists.'); diff --git a/src/Validator/Constraint/UniqueLoginName.php b/src/Identity/Validator/Constraint/UniqueLoginName.php similarity index 91% rename from src/Validator/Constraint/UniqueLoginName.php rename to src/Identity/Validator/Constraint/UniqueLoginName.php index 83c52a7..8c895e9 100644 --- a/src/Validator/Constraint/UniqueLoginName.php +++ b/src/Identity/Validator/Constraint/UniqueLoginName.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Validator\Constraint; +namespace PhpList\RestBundle\Identity\Validator\Constraint; use Symfony\Component\Validator\Constraint; diff --git a/src/Validator/Constraint/UniqueLoginNameValidator.php b/src/Identity/Validator/Constraint/UniqueLoginNameValidator.php similarity index 96% rename from src/Validator/Constraint/UniqueLoginNameValidator.php rename to src/Identity/Validator/Constraint/UniqueLoginNameValidator.php index b0e0af7..ba8727e 100644 --- a/src/Validator/Constraint/UniqueLoginNameValidator.php +++ b/src/Identity/Validator/Constraint/UniqueLoginNameValidator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Validator\Constraint; +namespace PhpList\RestBundle\Identity\Validator\Constraint; use PhpList\Core\Domain\Identity\Repository\AdministratorRepository; use Symfony\Component\HttpKernel\Exception\ConflictHttpException; diff --git a/src/Controller/CampaignController.php b/src/Messaging/Controller/CampaignController.php similarity index 96% rename from src/Controller/CampaignController.php rename to src/Messaging/Controller/CampaignController.php index e8c0a62..ff28c8e 100644 --- a/src/Controller/CampaignController.php +++ b/src/Messaging/Controller/CampaignController.php @@ -2,18 +2,19 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Messaging\Controller; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\Filter\MessageFilter; use PhpList\Core\Domain\Messaging\Model\Message; use PhpList\Core\Domain\Messaging\Service\MessageManager; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Entity\Request\CreateMessageRequest; -use PhpList\RestBundle\Entity\Request\UpdateMessageRequest; -use PhpList\RestBundle\Serializer\MessageNormalizer; -use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; -use PhpList\RestBundle\Validator\RequestValidator; +use PhpList\RestBundle\Common\Controller\BaseController; +use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider; +use PhpList\RestBundle\Common\Validator\RequestValidator; +use PhpList\RestBundle\Messaging\Request\CreateMessageRequest; +use PhpList\RestBundle\Messaging\Request\UpdateMessageRequest; +use PhpList\RestBundle\Messaging\Serializer\MessageNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Controller/TemplateController.php b/src/Messaging/Controller/TemplateController.php similarity index 96% rename from src/Controller/TemplateController.php rename to src/Messaging/Controller/TemplateController.php index b18bcc8..00ceb34 100644 --- a/src/Controller/TemplateController.php +++ b/src/Messaging/Controller/TemplateController.php @@ -2,16 +2,17 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Messaging\Controller; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\Template; use PhpList\Core\Domain\Messaging\Service\TemplateManager; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Entity\Request\CreateTemplateRequest; -use PhpList\RestBundle\Serializer\TemplateNormalizer; -use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; -use PhpList\RestBundle\Validator\RequestValidator; +use PhpList\RestBundle\Common\Controller\BaseController; +use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider; +use PhpList\RestBundle\Common\Validator\RequestValidator; +use PhpList\RestBundle\Messaging\Request\CreateTemplateRequest; +use PhpList\RestBundle\Messaging\Serializer\TemplateNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/OpenApi/SwaggerSchemasRequestDto.php b/src/Messaging/OpenApi/SwaggerSchemasRequest.php similarity index 61% rename from src/OpenApi/SwaggerSchemasRequestDto.php rename to src/Messaging/OpenApi/SwaggerSchemasRequest.php index 813a89d..8aef402 100644 --- a/src/OpenApi/SwaggerSchemasRequestDto.php +++ b/src/Messaging/OpenApi/SwaggerSchemasRequest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\OpenApi; +namespace PhpList\RestBundle\Messaging\OpenApi; use OpenApi\Attributes as OA; @@ -77,71 +77,6 @@ enum: ['html', 'text', 'invite'], ], type: 'object' )] -#[OA\Schema( - schema: 'CreateAdministratorRequest', - required: ['login_name', 'password', 'email', 'super_user'], - properties: [ - new OA\Property( - property: 'login_name', - type: 'string', - maxLength: 255, - minLength: 3, - example: 'admin' - ), - new OA\Property( - property: 'password', - type: 'string', - format: 'password', - maxLength: 255, - minLength: 6, - example: 'StrongP@ssw0rd' - ), - new OA\Property( - property: 'email', - type: 'string', - format: 'email', - example: 'admin@example.com' - ), - new OA\Property( - property: 'super_user', - type: 'boolean', - example: false - ), - ], - type: 'object' -)] -#[OA\Schema( - schema: 'UpdateAdministratorRequest', - properties: [ - new OA\Property( - property: 'login_name', - type: 'string', - maxLength: 255, - minLength: 3, - example: 'admin' - ), - new OA\Property( - property: 'password', - type: 'string', - format: 'password', - maxLength: 255, - minLength: 6, - example: 'StrongP@ssw0rd' - ), - new OA\Property( - property: 'email', - type: 'string', - format: 'email', - example: 'admin@example.com' - ), - new OA\Property( - property: 'super_user', - type: 'boolean', - example: false - ), - ], - type: 'object' -)] -class SwaggerSchemasRequestDto +class SwaggerSchemasRequest { } diff --git a/src/OpenApi/SwaggerSchemasResponseEntity.php b/src/Messaging/OpenApi/SwaggerSchemasResponse.php similarity index 60% rename from src/OpenApi/SwaggerSchemasResponseEntity.php rename to src/Messaging/OpenApi/SwaggerSchemasResponse.php index d9652bd..fe567c8 100644 --- a/src/OpenApi/SwaggerSchemasResponseEntity.php +++ b/src/Messaging/OpenApi/SwaggerSchemasResponse.php @@ -2,65 +2,10 @@ declare(strict_types=1); -namespace PhpList\RestBundle\OpenApi; +namespace PhpList\RestBundle\Messaging\OpenApi; use OpenApi\Attributes as OA; -#[OA\Schema( - schema: 'SubscriberList', - properties: [ - new OA\Property(property: 'id', type: 'integer', example: 2), - new OA\Property(property: 'name', type: 'string', example: 'Newsletter'), - new OA\Property(property: 'description', type: 'string', example: 'Monthly updates'), - new OA\Property( - property: 'created_at', - type: 'string', - format: 'date-time', - example: '2022-12-01T10:00:00Z' - ), - new OA\Property(property: 'public', type: 'boolean', example: true), - ], - type: 'object' -)] -#[OA\Schema( - schema: 'Subscriber', - properties: [ - new OA\Property(property: 'id', type: 'integer', example: 1), - new OA\Property(property: 'email', type: 'string', example: 'subscriber@example.com'), - new OA\Property( - property: 'created_at', - type: 'string', - format: 'date-time', - example: '2023-01-01T12:00:00Z', - ), - new OA\Property(property: 'confirmed', type: 'boolean', example: true), - new OA\Property(property: 'blacklisted', type: 'boolean', example: false), - new OA\Property(property: 'bounce_count', type: 'integer', example: 0), - new OA\Property(property: 'unique_id', type: 'string', example: '69f4e92cf50eafca9627f35704f030f4'), - new OA\Property(property: 'html_email', type: 'boolean', example: true), - new OA\Property(property: 'disabled', type: 'boolean', example: false), - new OA\Property( - property: 'subscribed_lists', - type: 'array', - items: new OA\Items(ref: '#/components/schemas/SubscriberList') - ), - ], - type: 'object' -)] -#[OA\Schema( - schema: 'Subscription', - properties: [ - new OA\Property(property: 'subscriber', ref: '#/components/schemas/Subscriber'), - new OA\Property(property: 'subscriber_list', ref: '#/components/schemas/SubscriberList'), - new OA\Property( - property: 'subscription_date', - type: 'string', - format: 'date-time', - example: '2023-01-01T12:00:00Z', - ), - ], - type: 'object' -)] #[OA\Schema( schema: 'TemplateImage', properties: [ @@ -174,35 +119,6 @@ ], type: 'object' )] -#[OA\Schema( - schema: 'Administrator', - properties: [ - new OA\Property(property: 'id', type: 'integer', example: 1), - new OA\Property(property: 'login_name', type: 'string', example: 'admin'), - new OA\Property(property: 'email', type: 'string', format: 'email', example: 'admin@example.com'), - new OA\Property(property: 'super_user', type: 'boolean', example: true), - new OA\Property( - property: 'created_at', - type: 'string', - format: 'date-time', - example: '2025-04-29T12:34:56+00:00' - ), - ], - type: 'object' -)] -#[OA\Schema( - schema: 'AttributeDefinition', - 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: 'checkbox'), - 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: 'list_attributes'), - ], - type: 'object' -)] -class SwaggerSchemasResponseEntity +class SwaggerSchemasResponse { } diff --git a/src/Entity/Request/CreateMessageRequest.php b/src/Messaging/Request/CreateMessageRequest.php similarity index 66% rename from src/Entity/Request/CreateMessageRequest.php rename to src/Messaging/Request/CreateMessageRequest.php index 8f6f5e2..4d07968 100644 --- a/src/Entity/Request/CreateMessageRequest.php +++ b/src/Messaging/Request/CreateMessageRequest.php @@ -2,16 +2,17 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request; +namespace PhpList\RestBundle\Messaging\Request; use PhpList\Core\Domain\Messaging\Model\Dto\CreateMessageDto; use PhpList\Core\Domain\Messaging\Model\Dto\MessageDtoInterface; -use PhpList\RestBundle\Entity\Request\Message\MessageContentRequest; -use PhpList\RestBundle\Entity\Request\Message\MessageFormatRequest; -use PhpList\RestBundle\Entity\Request\Message\MessageMetadataRequest; -use PhpList\RestBundle\Entity\Request\Message\MessageOptionsRequest; -use PhpList\RestBundle\Entity\Request\Message\MessageScheduleRequest; -use PhpList\RestBundle\Validator\Constraint as CustomAssert; +use PhpList\RestBundle\Common\Request\RequestInterface; +use PhpList\RestBundle\Messaging\Request\Message\MessageContentRequest; +use PhpList\RestBundle\Messaging\Request\Message\MessageFormatRequest; +use PhpList\RestBundle\Messaging\Request\Message\MessageMetadataRequest; +use PhpList\RestBundle\Messaging\Request\Message\MessageOptionsRequest; +use PhpList\RestBundle\Messaging\Request\Message\MessageScheduleRequest; +use PhpList\RestBundle\Messaging\Validator\Constraint\TemplateExists; use Symfony\Component\Validator\Constraints as Assert; class CreateMessageRequest implements RequestInterface @@ -36,7 +37,7 @@ class CreateMessageRequest implements RequestInterface #[Assert\NotNull] public MessageOptionsRequest $options; - #[CustomAssert\TemplateExists] + #[TemplateExists] public ?int $templateId; public function getDto(): MessageDtoInterface diff --git a/src/Entity/Request/CreateTemplateRequest.php b/src/Messaging/Request/CreateTemplateRequest.php similarity index 80% rename from src/Entity/Request/CreateTemplateRequest.php rename to src/Messaging/Request/CreateTemplateRequest.php index b68e31c..c5bbbcb 100644 --- a/src/Entity/Request/CreateTemplateRequest.php +++ b/src/Messaging/Request/CreateTemplateRequest.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request; +namespace PhpList\RestBundle\Messaging\Request; use PhpList\Core\Domain\Messaging\Model\Dto\CreateTemplateDto; -use PhpList\RestBundle\Validator\Constraint as CustomAssert; +use PhpList\RestBundle\Common\Request\RequestInterface; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\Validator\Constraints as Assert; @@ -16,10 +16,10 @@ class CreateTemplateRequest implements RequestInterface public string $title; #[Assert\NotBlank] - #[CustomAssert\ContainsPlaceholder] + #[\PhpList\RestBundle\Messaging\Validator\Constraint\ContainsPlaceholder] public string $content; - #[CustomAssert\ContainsPlaceholder] + #[\PhpList\RestBundle\Messaging\Validator\Constraint\ContainsPlaceholder] public ?string $text = null; public ?UploadedFile $file = null; diff --git a/src/Entity/Request/Message/MessageContentRequest.php b/src/Messaging/Request/Message/MessageContentRequest.php similarity index 92% rename from src/Entity/Request/Message/MessageContentRequest.php rename to src/Messaging/Request/Message/MessageContentRequest.php index ba2c616..9363058 100644 --- a/src/Entity/Request/Message/MessageContentRequest.php +++ b/src/Messaging/Request/Message/MessageContentRequest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request\Message; +namespace PhpList\RestBundle\Messaging\Request\Message; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageContentDto; use Symfony\Component\Validator\Constraints as Assert; diff --git a/src/Entity/Request/Message/MessageFormatRequest.php b/src/Messaging/Request/Message/MessageFormatRequest.php similarity index 93% rename from src/Entity/Request/Message/MessageFormatRequest.php rename to src/Messaging/Request/Message/MessageFormatRequest.php index 2a6f429..3dd20d8 100644 --- a/src/Entity/Request/Message/MessageFormatRequest.php +++ b/src/Messaging/Request/Message/MessageFormatRequest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request\Message; +namespace PhpList\RestBundle\Messaging\Request\Message; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageFormatDto; use Symfony\Component\Validator\Constraints as Assert; diff --git a/src/Entity/Request/Message/MessageMetadataRequest.php b/src/Messaging/Request/Message/MessageMetadataRequest.php similarity index 88% rename from src/Entity/Request/Message/MessageMetadataRequest.php rename to src/Messaging/Request/Message/MessageMetadataRequest.php index d16d962..ca908e6 100644 --- a/src/Entity/Request/Message/MessageMetadataRequest.php +++ b/src/Messaging/Request/Message/MessageMetadataRequest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request\Message; +namespace PhpList\RestBundle\Messaging\Request\Message; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageMetadataDto; use Symfony\Component\Validator\Constraints as Assert; diff --git a/src/Entity/Request/Message/MessageOptionsRequest.php b/src/Messaging/Request/Message/MessageOptionsRequest.php similarity index 92% rename from src/Entity/Request/Message/MessageOptionsRequest.php rename to src/Messaging/Request/Message/MessageOptionsRequest.php index 044feb8..f2f3485 100644 --- a/src/Entity/Request/Message/MessageOptionsRequest.php +++ b/src/Messaging/Request/Message/MessageOptionsRequest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request\Message; +namespace PhpList\RestBundle\Messaging\Request\Message; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageOptionsDto; use Symfony\Component\Validator\Constraints as Assert; diff --git a/src/Entity/Request/Message/MessageScheduleRequest.php b/src/Messaging/Request/Message/MessageScheduleRequest.php similarity index 93% rename from src/Entity/Request/Message/MessageScheduleRequest.php rename to src/Messaging/Request/Message/MessageScheduleRequest.php index 860f40e..ed6aa8e 100644 --- a/src/Entity/Request/Message/MessageScheduleRequest.php +++ b/src/Messaging/Request/Message/MessageScheduleRequest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request\Message; +namespace PhpList\RestBundle\Messaging\Request\Message; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageScheduleDto; use Symfony\Component\Validator\Constraints as Assert; diff --git a/src/Entity/Request/Message/RequestDtoInterface.php b/src/Messaging/Request/Message/RequestDtoInterface.php similarity index 55% rename from src/Entity/Request/Message/RequestDtoInterface.php rename to src/Messaging/Request/Message/RequestDtoInterface.php index d4a723a..e85a27e 100644 --- a/src/Entity/Request/Message/RequestDtoInterface.php +++ b/src/Messaging/Request/Message/RequestDtoInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request\Message; +namespace PhpList\RestBundle\Messaging\Request\Message; interface RequestDtoInterface { diff --git a/src/Entity/Request/UpdateMessageRequest.php b/src/Messaging/Request/UpdateMessageRequest.php similarity index 93% rename from src/Entity/Request/UpdateMessageRequest.php rename to src/Messaging/Request/UpdateMessageRequest.php index f0b4f59..3f20dc2 100644 --- a/src/Entity/Request/UpdateMessageRequest.php +++ b/src/Messaging/Request/UpdateMessageRequest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Entity\Request; +namespace PhpList\RestBundle\Messaging\Request; use PhpList\Core\Domain\Messaging\Model\Dto\MessageDtoInterface; use PhpList\Core\Domain\Messaging\Model\Dto\UpdateMessageDto; diff --git a/src/Serializer/MessageNormalizer.php b/src/Messaging/Serializer/MessageNormalizer.php similarity index 98% rename from src/Serializer/MessageNormalizer.php rename to src/Messaging/Serializer/MessageNormalizer.php index 0383afa..b659b3d 100644 --- a/src/Serializer/MessageNormalizer.php +++ b/src/Messaging/Serializer/MessageNormalizer.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Serializer; +namespace PhpList\RestBundle\Messaging\Serializer; use PhpList\Core\Domain\Messaging\Model\Message; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; diff --git a/src/Serializer/TemplateImageNormalizer.php b/src/Messaging/Serializer/TemplateImageNormalizer.php similarity index 95% rename from src/Serializer/TemplateImageNormalizer.php rename to src/Messaging/Serializer/TemplateImageNormalizer.php index fd0888b..64ced9a 100644 --- a/src/Serializer/TemplateImageNormalizer.php +++ b/src/Messaging/Serializer/TemplateImageNormalizer.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Serializer; +namespace PhpList\RestBundle\Messaging\Serializer; use PhpList\Core\Domain\Messaging\Model\TemplateImage; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; diff --git a/src/Serializer/TemplateNormalizer.php b/src/Messaging/Serializer/TemplateNormalizer.php similarity index 96% rename from src/Serializer/TemplateNormalizer.php rename to src/Messaging/Serializer/TemplateNormalizer.php index 6cf988c..669f7a4 100644 --- a/src/Serializer/TemplateNormalizer.php +++ b/src/Messaging/Serializer/TemplateNormalizer.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Serializer; +namespace PhpList\RestBundle\Messaging\Serializer; use PhpList\Core\Domain\Messaging\Model\Template; use PhpList\Core\Domain\Messaging\Model\TemplateImage; diff --git a/src/Validator/Constraint/ContainsPlaceholder.php b/src/Messaging/Validator/Constraint/ContainsPlaceholder.php similarity index 86% rename from src/Validator/Constraint/ContainsPlaceholder.php rename to src/Messaging/Validator/Constraint/ContainsPlaceholder.php index f6179ed..613441b 100644 --- a/src/Validator/Constraint/ContainsPlaceholder.php +++ b/src/Messaging/Validator/Constraint/ContainsPlaceholder.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Validator\Constraint; +namespace PhpList\RestBundle\Messaging\Validator\Constraint; use Symfony\Component\Validator\Constraint; diff --git a/src/Validator/Constraint/ContainsPlaceholderValidator.php b/src/Messaging/Validator/Constraint/ContainsPlaceholderValidator.php similarity index 93% rename from src/Validator/Constraint/ContainsPlaceholderValidator.php rename to src/Messaging/Validator/Constraint/ContainsPlaceholderValidator.php index 75877f6..c33a65d 100644 --- a/src/Validator/Constraint/ContainsPlaceholderValidator.php +++ b/src/Messaging/Validator/Constraint/ContainsPlaceholderValidator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Validator\Constraint; +namespace PhpList\RestBundle\Messaging\Validator\Constraint; use InvalidArgumentException; use Symfony\Component\Validator\Constraint; diff --git a/src/Validator/Constraint/TemplateExists.php b/src/Messaging/Validator/Constraint/TemplateExists.php similarity index 91% rename from src/Validator/Constraint/TemplateExists.php rename to src/Messaging/Validator/Constraint/TemplateExists.php index b46b395..7585223 100644 --- a/src/Validator/Constraint/TemplateExists.php +++ b/src/Messaging/Validator/Constraint/TemplateExists.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Validator\Constraint; +namespace PhpList\RestBundle\Messaging\Validator\Constraint; use Symfony\Component\Validator\Constraint; diff --git a/src/Validator/Constraint/TemplateExistsValidator.php b/src/Messaging/Validator/Constraint/TemplateExistsValidator.php similarity index 95% rename from src/Validator/Constraint/TemplateExistsValidator.php rename to src/Messaging/Validator/Constraint/TemplateExistsValidator.php index cf23949..7e211c2 100644 --- a/src/Validator/Constraint/TemplateExistsValidator.php +++ b/src/Messaging/Validator/Constraint/TemplateExistsValidator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Validator\Constraint; +namespace PhpList\RestBundle\Messaging\Validator\Constraint; use PhpList\Core\Domain\Messaging\Repository\TemplateRepository; use Symfony\Component\HttpKernel\Exception\ConflictHttpException; diff --git a/src/Controller/ListController.php b/src/Subscription/Controller/ListController.php similarity index 92% rename from src/Controller/ListController.php rename to src/Subscription/Controller/ListController.php index eafea3a..9b3e0a3 100644 --- a/src/Controller/ListController.php +++ b/src/Subscription/Controller/ListController.php @@ -2,16 +2,17 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Subscription\Controller; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\Core\Domain\Subscription\Service\SubscriberListManager; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Entity\Request\CreateSubscriberListRequest; -use PhpList\RestBundle\Serializer\SubscriberListNormalizer; -use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; -use PhpList\RestBundle\Validator\RequestValidator; +use PhpList\RestBundle\Common\Controller\BaseController; +use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider; +use PhpList\RestBundle\Common\Validator\RequestValidator; +use PhpList\RestBundle\Subscription\Request\CreateSubscriberListRequest; +use PhpList\RestBundle\Subscription\Serializer\SubscriberListNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -233,15 +234,7 @@ public function deleteList( requestBody: new OA\RequestBody( description: 'Pass parameters to create a new subscriber list.', required: true, - content: new OA\JsonContent( - required: ['name'], - properties: [ - new OA\Property(property: 'name', type: 'string', format: 'string', example: 'News'), - new OA\Property(property: 'description', type: 'string', example: 'News (and some fun stuff)'), - new OA\Property(property: 'list_position', type: 'number', example: 12), - new OA\Property(property: 'public', type: 'boolean', example: true), - ] - ) + content: new OA\JsonContent(ref: '#/components/schemas/CreateSubscriberListRequest') ), tags: ['lists'], parameters: [ diff --git a/src/Controller/ListMembersController.php b/src/Subscription/Controller/ListMembersController.php similarity index 95% rename from src/Controller/ListMembersController.php rename to src/Subscription/Controller/ListMembersController.php index 5aa65a0..1e2403c 100644 --- a/src/Controller/ListMembersController.php +++ b/src/Subscription/Controller/ListMembersController.php @@ -2,16 +2,17 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Subscription\Controller; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\Filter\SubscriberFilter; use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Serializer\SubscriberNormalizer; -use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; -use PhpList\RestBundle\Validator\RequestValidator; +use PhpList\RestBundle\Common\Controller\BaseController; +use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider; +use PhpList\RestBundle\Common\Validator\RequestValidator; +use PhpList\RestBundle\Subscription\Serializer\SubscriberNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Controller/SubscriberAttributeDefinitionController.php b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php similarity index 96% rename from src/Controller/SubscriberAttributeDefinitionController.php rename to src/Subscription/Controller/SubscriberAttributeDefinitionController.php index 195b529..479e9a9 100644 --- a/src/Controller/SubscriberAttributeDefinitionController.php +++ b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php @@ -2,17 +2,18 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Subscription\Controller; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition; use PhpList\Core\Domain\Subscription\Service\AttributeDefinitionManager; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Entity\Request\CreateAttributeDefinitionRequest; -use PhpList\RestBundle\Entity\Request\UpdateAttributeDefinitionRequest; -use PhpList\RestBundle\Serializer\AttributeDefinitionNormalizer; -use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; -use PhpList\RestBundle\Validator\RequestValidator; +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\Serializer\AttributeDefinitionNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Controller/SubscriberController.php b/src/Subscription/Controller/SubscriberController.php similarity index 96% rename from src/Controller/SubscriberController.php rename to src/Subscription/Controller/SubscriberController.php index 8a4fb9c..c18cb2f 100644 --- a/src/Controller/SubscriberController.php +++ b/src/Subscription/Controller/SubscriberController.php @@ -2,16 +2,17 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Subscription\Controller; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Service\SubscriberManager; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Entity\Request\CreateSubscriberRequest; -use PhpList\RestBundle\Entity\Request\UpdateSubscriberRequest; -use PhpList\RestBundle\Serializer\SubscriberNormalizer; -use PhpList\RestBundle\Validator\RequestValidator; +use PhpList\RestBundle\Common\Controller\BaseController; +use PhpList\RestBundle\Common\Validator\RequestValidator; +use PhpList\RestBundle\Subscription\Request\CreateSubscriberRequest; +use PhpList\RestBundle\Subscription\Request\UpdateSubscriberRequest; +use PhpList\RestBundle\Subscription\Serializer\SubscriberNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Controller/SubscriptionController.php b/src/Subscription/Controller/SubscriptionController.php similarity index 95% rename from src/Controller/SubscriptionController.php rename to src/Subscription/Controller/SubscriptionController.php index c9a4e95..3a78237 100644 --- a/src/Controller/SubscriptionController.php +++ b/src/Subscription/Controller/SubscriptionController.php @@ -2,15 +2,16 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Controller; +namespace PhpList\RestBundle\Subscription\Controller; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\Core\Domain\Subscription\Service\SubscriptionManager; use PhpList\Core\Security\Authentication; -use PhpList\RestBundle\Entity\Request\SubscriptionRequest; -use PhpList\RestBundle\Serializer\SubscriptionNormalizer; -use PhpList\RestBundle\Validator\RequestValidator; +use PhpList\RestBundle\Common\Controller\BaseController; +use PhpList\RestBundle\Common\Validator\RequestValidator; +use PhpList\RestBundle\Subscription\Request\SubscriptionRequest; +use PhpList\RestBundle\Subscription\Serializer\SubscriptionNormalizer; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Subscription/OpenApi/SwaggerSchemasRequest.php b/src/Subscription/OpenApi/SwaggerSchemasRequest.php new file mode 100644 index 0000000..6009711 --- /dev/null +++ b/src/Subscription/OpenApi/SwaggerSchemasRequest.php @@ -0,0 +1,22 @@ +entityClass = $entityClass; + } + + public function validatedBy(): string + { + return UniqueEmailValidator::class; + } +} diff --git a/src/Subscription/Validator/Constraint/UniqueEmailValidator.php b/src/Subscription/Validator/Constraint/UniqueEmailValidator.php new file mode 100644 index 0000000..46bf52b --- /dev/null +++ b/src/Subscription/Validator/Constraint/UniqueEmailValidator.php @@ -0,0 +1,45 @@ +entityManager + ->getRepository($constraint->entityClass) + ->findOneBy(['email' => $value]); + + $dto = $this->context->getObject(); + $updatingId = $dto->subscriberId ?? null; + + if ($existingUser && $existingUser->getId() !== $updatingId) { + throw new ConflictHttpException('Email already exists.'); + } + } +} diff --git a/tests/Helpers/DummyRequestDto.php b/tests/Helpers/DummyRequestDto.php index 00a695f..13aa955 100644 --- a/tests/Helpers/DummyRequestDto.php +++ b/tests/Helpers/DummyRequestDto.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Tests\Helpers; -use PhpList\RestBundle\Entity\Request\RequestInterface; +use PhpList\RestBundle\Common\Request\RequestInterface; class DummyRequestDto implements RequestInterface { diff --git a/tests/Integration/Composer/ScriptsTest.php b/tests/Integration/Composer/ScriptsTest.php index 9a077ac..436b4d1 100644 --- a/tests/Integration/Composer/ScriptsTest.php +++ b/tests/Integration/Composer/ScriptsTest.php @@ -107,6 +107,7 @@ private function getModuleRoutesConfigurationFilePath(): string { return dirname(__DIR__, 3) . '/config/routing_modules.yml'; } + public function testModuleRoutesConfigurationFileExists() { self::assertFileExists($this->getModuleRoutesConfigurationFilePath()); @@ -119,7 +120,9 @@ public static function moduleRoutingDataProvider(): array { return [ 'route name' => ['phplist/rest-api.rest-api'], - 'resource' => ["resource: '@PhpListRestBundle/Controller/'"], + 'identity' => ["resource: '@PhpListRestBundle/Identity/Controller/'"], + 'messaging' => ["resource: '@PhpListRestBundle/Messaging/Controller/'"], + 'subscription' => ["resource: '@PhpListRestBundle/Subscription/Controller/'"], 'type' => ['type: attribute'], ]; } diff --git a/tests/Integration/Controller/AttributeDefinitionControllerTest.php b/tests/Integration/Controller/AttributeDefinitionControllerTest.php index e117119..d680e36 100644 --- a/tests/Integration/Controller/AttributeDefinitionControllerTest.php +++ b/tests/Integration/Controller/AttributeDefinitionControllerTest.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Tests\Integration\Controller; use PhpList\Core\Domain\Subscription\Repository\SubscriberAttributeDefinitionRepository; -use PhpList\RestBundle\Controller\SubscriberAttributeDefinitionController; +use PhpList\RestBundle\Subscription\Controller\SubscriberAttributeDefinitionController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\AttributeDefinitionFixture; diff --git a/tests/Integration/Controller/CampaignControllerTest.php b/tests/Integration/Controller/CampaignControllerTest.php index 9949b7c..c63d41f 100644 --- a/tests/Integration/Controller/CampaignControllerTest.php +++ b/tests/Integration/Controller/CampaignControllerTest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Tests\Integration\Controller; -use PhpList\RestBundle\Controller\CampaignController; +use PhpList\RestBundle\Messaging\Controller\CampaignController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\MessageFixture; diff --git a/tests/Integration/Controller/ListControllerTest.php b/tests/Integration/Controller/ListControllerTest.php index cffe7fd..f254831 100644 --- a/tests/Integration/Controller/ListControllerTest.php +++ b/tests/Integration/Controller/ListControllerTest.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Tests\Integration\Controller; use PhpList\Core\Domain\Subscription\Repository\SubscriberListRepository; -use PhpList\RestBundle\Controller\ListController; +use PhpList\RestBundle\Subscription\Controller\ListController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\SubscriberListFixture; diff --git a/tests/Integration/Controller/ListMembersControllerTest.php b/tests/Integration/Controller/ListMembersControllerTest.php index 1586ba3..d40ae2a 100644 --- a/tests/Integration/Controller/ListMembersControllerTest.php +++ b/tests/Integration/Controller/ListMembersControllerTest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Tests\Integration\Controller; -use PhpList\RestBundle\Controller\ListMembersController; +use PhpList\RestBundle\Subscription\Controller\ListMembersController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\SubscriberListFixture; diff --git a/tests/Integration/Controller/SessionControllerTest.php b/tests/Integration/Controller/SessionControllerTest.php index d0c760a..3121514 100644 --- a/tests/Integration/Controller/SessionControllerTest.php +++ b/tests/Integration/Controller/SessionControllerTest.php @@ -7,7 +7,7 @@ use DateTime; use PhpList\Core\Domain\Identity\Model\AdministratorToken; use PhpList\Core\Domain\Identity\Repository\AdministratorTokenRepository; -use PhpList\RestBundle\Controller\SessionController; +use PhpList\RestBundle\Identity\Controller\SessionController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; diff --git a/tests/Integration/Controller/SubscriberControllerTest.php b/tests/Integration/Controller/SubscriberControllerTest.php index 5303c5f..380fa9e 100644 --- a/tests/Integration/Controller/SubscriberControllerTest.php +++ b/tests/Integration/Controller/SubscriberControllerTest.php @@ -6,7 +6,7 @@ use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository; -use PhpList\RestBundle\Controller\SubscriberController; +use PhpList\RestBundle\Subscription\Controller\SubscriberController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\SubscriberFixture; /** diff --git a/tests/Integration/Controller/SubscriptionControllerTest.php b/tests/Integration/Controller/SubscriptionControllerTest.php index f43d396..e985134 100644 --- a/tests/Integration/Controller/SubscriptionControllerTest.php +++ b/tests/Integration/Controller/SubscriptionControllerTest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Tests\Integration\Controller; -use PhpList\RestBundle\Controller\SubscriptionController; +use PhpList\RestBundle\Subscription\Controller\SubscriptionController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\SubscriberListFixture; diff --git a/tests/Integration/Controller/TemplateControllerTest.php b/tests/Integration/Controller/TemplateControllerTest.php index d523777..53313a7 100644 --- a/tests/Integration/Controller/TemplateControllerTest.php +++ b/tests/Integration/Controller/TemplateControllerTest.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Tests\Integration\Controller; use PhpList\Core\Domain\Messaging\Repository\TemplateRepository; -use PhpList\RestBundle\Controller\TemplateController; +use PhpList\RestBundle\Messaging\Controller\TemplateController; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\TemplateFixture; diff --git a/tests/Integration/EventListener/ExceptionListenerTest.php b/tests/Integration/EventListener/ExceptionListenerTest.php index ea346ff..be1d7d9 100644 --- a/tests/Integration/EventListener/ExceptionListenerTest.php +++ b/tests/Integration/EventListener/ExceptionListenerTest.php @@ -5,14 +5,14 @@ namespace PhpList\RestBundle\Tests\Integration\EventListener; use PhpList\Core\Domain\Subscription\Exception\SubscriptionCreationException; -use PhpList\RestBundle\EventListener\ExceptionListener; +use PhpList\RestBundle\Common\EventListener\ExceptionListener; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\ExceptionEvent; -use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; use Throwable; class ExceptionListenerTest extends TestCase diff --git a/tests/Integration/EventListener/ResponseListenerTest.php b/tests/Integration/EventListener/ResponseListenerTest.php index 709291a..a037b09 100644 --- a/tests/Integration/EventListener/ResponseListenerTest.php +++ b/tests/Integration/EventListener/ResponseListenerTest.php @@ -2,7 +2,7 @@ namespace PhpList\RestBundle\Tests\Integration\EventListener; -use PhpList\RestBundle\EventListener\ResponseListener; +use PhpList\RestBundle\Common\EventListener\ResponseListener; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/tests/Unit/Serializer/AdministratorNormalizerTest.php b/tests/Unit/Serializer/AdministratorNormalizerTest.php index f63870b..efe89bc 100644 --- a/tests/Unit/Serializer/AdministratorNormalizerTest.php +++ b/tests/Unit/Serializer/AdministratorNormalizerTest.php @@ -7,7 +7,7 @@ use DateTime; use InvalidArgumentException; use PhpList\Core\Domain\Identity\Model\Administrator; -use PhpList\RestBundle\Serializer\AdministratorNormalizer; +use PhpList\RestBundle\Identity\Serializer\AdministratorNormalizer; use PHPUnit\Framework\TestCase; class AdministratorNormalizerTest extends TestCase diff --git a/tests/Unit/Serializer/AdministratorTokenNormalizerTest.php b/tests/Unit/Serializer/AdministratorTokenNormalizerTest.php index 0361be5..788e7cd 100644 --- a/tests/Unit/Serializer/AdministratorTokenNormalizerTest.php +++ b/tests/Unit/Serializer/AdministratorTokenNormalizerTest.php @@ -6,7 +6,7 @@ use DateTime; use PhpList\Core\Domain\Identity\Model\AdministratorToken; -use PhpList\RestBundle\Serializer\AdministratorTokenNormalizer; +use PhpList\RestBundle\Identity\Serializer\AdministratorTokenNormalizer; use PHPUnit\Framework\TestCase; class AdministratorTokenNormalizerTest extends TestCase diff --git a/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php b/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php index 36c5cb1..58966d1 100644 --- a/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php +++ b/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition; -use PhpList\RestBundle\Serializer\AttributeDefinitionNormalizer; +use PhpList\RestBundle\Subscription\Serializer\AttributeDefinitionNormalizer; use PHPUnit\Framework\TestCase; class AttributeDefinitionNormalizerTest extends TestCase diff --git a/tests/Unit/Serializer/CursorPaginationNormalizerTest.php b/tests/Unit/Serializer/CursorPaginationNormalizerTest.php index 49054e7..45a8419 100644 --- a/tests/Unit/Serializer/CursorPaginationNormalizerTest.php +++ b/tests/Unit/Serializer/CursorPaginationNormalizerTest.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; -use PhpList\RestBundle\Entity\Dto\CursorPaginationResult; -use PhpList\RestBundle\Serializer\CursorPaginationNormalizer; +use PhpList\RestBundle\Common\Dto\CursorPaginationResult; +use PhpList\RestBundle\Common\Serializer\CursorPaginationNormalizer; use PHPUnit\Framework\TestCase; class CursorPaginationNormalizerTest extends TestCase diff --git a/tests/Unit/Serializer/MessageNormalizerTest.php b/tests/Unit/Serializer/MessageNormalizerTest.php index 3981eae..90e255f 100644 --- a/tests/Unit/Serializer/MessageNormalizerTest.php +++ b/tests/Unit/Serializer/MessageNormalizerTest.php @@ -7,9 +7,9 @@ use DateTime; use PhpList\Core\Domain\Messaging\Model\Message; use PhpList\Core\Domain\Messaging\Model\Template; -use PhpList\RestBundle\Serializer\MessageNormalizer; -use PhpList\RestBundle\Serializer\TemplateImageNormalizer; -use PhpList\RestBundle\Serializer\TemplateNormalizer; +use PhpList\RestBundle\Messaging\Serializer\MessageNormalizer; +use PhpList\RestBundle\Messaging\Serializer\TemplateImageNormalizer; +use PhpList\RestBundle\Messaging\Serializer\TemplateNormalizer; use PHPUnit\Framework\TestCase; class MessageNormalizerTest extends TestCase diff --git a/tests/Unit/Serializer/SubscriberListNormalizerTest.php b/tests/Unit/Serializer/SubscriberListNormalizerTest.php index d24fdd7..f17a3d9 100644 --- a/tests/Unit/Serializer/SubscriberListNormalizerTest.php +++ b/tests/Unit/Serializer/SubscriberListNormalizerTest.php @@ -6,7 +6,7 @@ use DateTime; use PhpList\Core\Domain\Subscription\Model\SubscriberList; -use PhpList\RestBundle\Serializer\SubscriberListNormalizer; +use PhpList\RestBundle\Subscription\Serializer\SubscriberListNormalizer; use PHPUnit\Framework\TestCase; class SubscriberListNormalizerTest extends TestCase diff --git a/tests/Unit/Serializer/SubscriberNormalizerTest.php b/tests/Unit/Serializer/SubscriberNormalizerTest.php index 9960fac..e0e100b 100644 --- a/tests/Unit/Serializer/SubscriberNormalizerTest.php +++ b/tests/Unit/Serializer/SubscriberNormalizerTest.php @@ -5,12 +5,12 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; use DateTime; +use Doctrine\Common\Collections\ArrayCollection; use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\Core\Domain\Subscription\Model\Subscription; -use PhpList\RestBundle\Serializer\SubscriberNormalizer; +use PhpList\RestBundle\Subscription\Serializer\SubscriberNormalizer; use PHPUnit\Framework\TestCase; -use Doctrine\Common\Collections\ArrayCollection; use stdClass; class SubscriberNormalizerTest extends TestCase diff --git a/tests/Unit/Serializer/SubscriptionNormalizerTest.php b/tests/Unit/Serializer/SubscriptionNormalizerTest.php index c192317..91e8970 100644 --- a/tests/Unit/Serializer/SubscriptionNormalizerTest.php +++ b/tests/Unit/Serializer/SubscriptionNormalizerTest.php @@ -8,9 +8,9 @@ use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\Core\Domain\Subscription\Model\Subscription; -use PhpList\RestBundle\Serializer\SubscriberListNormalizer; -use PhpList\RestBundle\Serializer\SubscriberNormalizer; -use PhpList\RestBundle\Serializer\SubscriptionNormalizer; +use PhpList\RestBundle\Subscription\Serializer\SubscriberListNormalizer; +use PhpList\RestBundle\Subscription\Serializer\SubscriberNormalizer; +use PhpList\RestBundle\Subscription\Serializer\SubscriptionNormalizer; use PHPUnit\Framework\TestCase; class SubscriptionNormalizerTest extends TestCase diff --git a/tests/Unit/Serializer/TemplateImageNormalizerTest.php b/tests/Unit/Serializer/TemplateImageNormalizerTest.php index 23f59d4..f6d7b6d 100644 --- a/tests/Unit/Serializer/TemplateImageNormalizerTest.php +++ b/tests/Unit/Serializer/TemplateImageNormalizerTest.php @@ -6,7 +6,7 @@ use PhpList\Core\Domain\Messaging\Model\Template; use PhpList\Core\Domain\Messaging\Model\TemplateImage; -use PhpList\RestBundle\Serializer\TemplateImageNormalizer; +use PhpList\RestBundle\Messaging\Serializer\TemplateImageNormalizer; use PHPUnit\Framework\TestCase; class TemplateImageNormalizerTest extends TestCase diff --git a/tests/Unit/Serializer/TemplateNormalizerTest.php b/tests/Unit/Serializer/TemplateNormalizerTest.php index 4b5ea76..8770509 100644 --- a/tests/Unit/Serializer/TemplateNormalizerTest.php +++ b/tests/Unit/Serializer/TemplateNormalizerTest.php @@ -4,13 +4,13 @@ namespace PhpList\RestBundle\Tests\Unit\Serializer; +use Doctrine\Common\Collections\ArrayCollection; use PhpList\Core\Domain\Messaging\Model\Template; use PhpList\Core\Domain\Messaging\Model\TemplateImage; -use PhpList\RestBundle\Serializer\TemplateImageNormalizer; -use PhpList\RestBundle\Serializer\TemplateNormalizer; +use PhpList\RestBundle\Messaging\Serializer\TemplateImageNormalizer; +use PhpList\RestBundle\Messaging\Serializer\TemplateNormalizer; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Doctrine\Common\Collections\ArrayCollection; class TemplateNormalizerTest extends TestCase { diff --git a/tests/Unit/Service/Factory/PaginationCursorRequestFactoryTest.php b/tests/Unit/Service/Factory/PaginationCursorRequestFactoryTest.php index 1cb6b35..f1a37de 100644 --- a/tests/Unit/Service/Factory/PaginationCursorRequestFactoryTest.php +++ b/tests/Unit/Service/Factory/PaginationCursorRequestFactoryTest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Tests\Unit\Service\Factory; -use PhpList\RestBundle\Service\Factory\PaginationCursorRequestFactory; +use PhpList\RestBundle\Common\Service\Factory\PaginationCursorRequestFactory; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; diff --git a/tests/Unit/Service/Provider/PaginatedDataProviderTest.php b/tests/Unit/Service/Provider/PaginatedDataProviderTest.php index 1175354..01f33ca 100644 --- a/tests/Unit/Service/Provider/PaginatedDataProviderTest.php +++ b/tests/Unit/Service/Provider/PaginatedDataProviderTest.php @@ -5,17 +5,17 @@ namespace PhpList\RestBundle\Tests\Unit\Service\Provider; use Doctrine\ORM\EntityManagerInterface; -use PhpList\RestBundle\Entity\Dto\CursorPaginationResult; -use PhpList\RestBundle\Entity\Request\PaginationCursorRequest; -use PhpList\RestBundle\Service\Factory\PaginationCursorRequestFactory; -use PhpList\RestBundle\Service\Provider\PaginatedDataProvider; -use PhpList\RestBundle\Serializer\CursorPaginationNormalizer; +use PhpList\RestBundle\Common\Dto\CursorPaginationResult; +use PhpList\RestBundle\Common\Request\PaginationCursorRequest; +use PhpList\RestBundle\Common\Serializer\CursorPaginationNormalizer; +use PhpList\RestBundle\Common\Service\Factory\PaginationCursorRequestFactory; +use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider; use PhpList\RestBundle\Tests\Helpers\DummyPaginatableRepository; use PhpList\RestBundle\Tests\Helpers\DummyRepository; use PHPUnit\Framework\TestCase; +use RuntimeException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; -use RuntimeException; class PaginatedDataProviderTest extends TestCase { diff --git a/tests/Unit/Validator/Constraint/ContainsPlaceholderValidatorTest.php b/tests/Unit/Validator/Constraint/ContainsPlaceholderValidatorTest.php index a4249db..f2bef35 100644 --- a/tests/Unit/Validator/Constraint/ContainsPlaceholderValidatorTest.php +++ b/tests/Unit/Validator/Constraint/ContainsPlaceholderValidatorTest.php @@ -4,8 +4,8 @@ namespace PhpList\RestBundle\Tests\Unit\Validator\Constraint; -use PhpList\RestBundle\Validator\Constraint\ContainsPlaceholder; -use PhpList\RestBundle\Validator\Constraint\ContainsPlaceholderValidator; +use PhpList\RestBundle\Messaging\Validator\Constraint\ContainsPlaceholder; +use PhpList\RestBundle\Messaging\Validator\Constraint\ContainsPlaceholderValidator; use PHPUnit\Framework\TestCase; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface; diff --git a/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php b/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php index 6b454c9..bcaf532 100644 --- a/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php +++ b/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php @@ -6,8 +6,8 @@ use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository; -use PhpList\RestBundle\Validator\Constraint\EmailExists; -use PhpList\RestBundle\Validator\Constraint\EmailExistsValidator; +use PhpList\RestBundle\Subscription\Validator\Constraint\EmailExists; +use PhpList\RestBundle\Subscription\Validator\Constraint\EmailExistsValidator; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; diff --git a/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php b/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php index ff17a6b..566a781 100644 --- a/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php +++ b/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php @@ -6,8 +6,8 @@ use PhpList\Core\Domain\Messaging\Model\Template; use PhpList\Core\Domain\Messaging\Repository\TemplateRepository; -use PhpList\RestBundle\Validator\Constraint\TemplateExists; -use PhpList\RestBundle\Validator\Constraint\TemplateExistsValidator; +use PhpList\RestBundle\Messaging\Validator\Constraint\TemplateExists; +use PhpList\RestBundle\Messaging\Validator\Constraint\TemplateExistsValidator; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpKernel\Exception\ConflictHttpException; diff --git a/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php b/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php index ffa7848..8a1fa10 100644 --- a/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php +++ b/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php @@ -7,8 +7,8 @@ use Doctrine\ORM\EntityManagerInterface; use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository; -use PhpList\RestBundle\Validator\Constraint\UniqueEmail; -use PhpList\RestBundle\Validator\Constraint\UniqueEmailValidator; +use PhpList\RestBundle\Subscription\Validator\Constraint\UniqueEmailValidator; +use PhpList\RestBundle\Subscription\Validator\Constraint\UniqueEmail; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpKernel\Exception\ConflictHttpException; diff --git a/tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php b/tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php index 90a75f7..897a7fd 100644 --- a/tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php +++ b/tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php @@ -6,11 +6,11 @@ use PhpList\Core\Domain\Identity\Model\Administrator; use PhpList\Core\Domain\Identity\Repository\AdministratorRepository; -use PhpList\RestBundle\Validator\Constraint\UniqueLoginName; -use PhpList\RestBundle\Validator\Constraint\UniqueLoginNameValidator; +use PhpList\RestBundle\Identity\Validator\Constraint\UniqueLoginName; +use PhpList\RestBundle\Identity\Validator\Constraint\UniqueLoginNameValidator; use PHPUnit\Framework\TestCase; -use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\HttpKernel\Exception\ConflictHttpException; +use Symfony\Component\Validator\Context\ExecutionContextInterface; class UniqueLoginNameValidatorTest extends TestCase { diff --git a/tests/Unit/Validator/RequestValidatorTest.php b/tests/Unit/Validator/RequestValidatorTest.php index d8ed2f3..ad93006 100644 --- a/tests/Unit/Validator/RequestValidatorTest.php +++ b/tests/Unit/Validator/RequestValidatorTest.php @@ -4,9 +4,9 @@ namespace PhpList\RestBundle\Tests\Unit\Validator; -use PhpList\RestBundle\Entity\Request\RequestInterface; +use PhpList\RestBundle\Common\Request\RequestInterface; +use PhpList\RestBundle\Common\Validator\RequestValidator; use PhpList\RestBundle\Tests\Helpers\DummyRequestDto; -use PhpList\RestBundle\Validator\RequestValidator; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; From 9377cb5848d02bf6cf5942d872085e09c292eaf2 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Mon, 12 May 2025 21:47:38 +0400 Subject: [PATCH 07/21] ISSUE-345: subscriber attribute relation --- .../Controller/ListMembersController.php | 2 +- .../SubscriberAttributeController.php | 333 ++++++++++++++++++ ...ubscriberAttributeDefinitionController.php | 26 +- .../OpenApi/SwaggerSchemasResponse.php | 8 + 4 files changed, 355 insertions(+), 14 deletions(-) create mode 100644 src/Subscription/Controller/SubscriberAttributeController.php diff --git a/src/Subscription/Controller/ListMembersController.php b/src/Subscription/Controller/ListMembersController.php index 1e2403c..636caee 100644 --- a/src/Subscription/Controller/ListMembersController.php +++ b/src/Subscription/Controller/ListMembersController.php @@ -55,7 +55,7 @@ public function __construct( description: 'List ID', in: 'path', required: true, - schema: new OA\Schema(type: 'string') + schema: new OA\Schema(type: 'integer') ), new OA\Parameter( name: 'after_id', diff --git a/src/Subscription/Controller/SubscriberAttributeController.php b/src/Subscription/Controller/SubscriberAttributeController.php new file mode 100644 index 0000000..df7ad2b --- /dev/null +++ b/src/Subscription/Controller/SubscriberAttributeController.php @@ -0,0 +1,333 @@ +attributeManager = $attributeManager; + $this->normalizer = $normalizer; + $this->paginatedDataProvider = $paginatedDataProvider; + } + + #[Route('/{subscriberId}/{definitionId}', name: 'create_subscriber_attribute_value', methods: ['POST', 'PUT'])] + #[OA\Post( + path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', + description: 'Returns created/updated subscriber attribute.', + summary: 'Create/update a subscriber attribute.', + requestBody: new OA\RequestBody( + description: 'Pass parameters to create subscriber attribute.', + required: true, + content: new OA\JsonContent( + properties: [ + new OA\Property(property: 'value', type: 'string', example: 'United States'), + ] + ) + ), + tags: ['subscriber-attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'definitionId', + description: 'attribute definition id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'subscriberId', + description: 'Subscriber id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + ], + responses: [ + new OA\Response( + response: 201, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/SubscriberAttributeValue') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 422, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse') + ), + ] + )] + public function createOrUpdate( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?SubscriberAttributeDefinition $definition = null, + #[MapEntity(mapping: ['subscriberId' => 'id'])] ?Subscriber $subscriber = null, + ): JsonResponse { + $this->requireAuthentication($request); + + if (!$definition) { + throw $this->createNotFoundException('Attribute definition not found.'); + } + if (!$subscriber) { + throw $this->createNotFoundException('Subscriber not found.'); + } + + $attributeDefinition = $this->attributeManager->createOrUpdate( + subscriber:$subscriber, + definition: $definition, + value: $request->get('value') + ); + $json = $this->normalizer->normalize($attributeDefinition, 'json'); + + return $this->json($json, Response::HTTP_CREATED); + } + + #[Route('/{subscriberId}/{definitionId}', name: 'delete_subscriber_attribute', methods: ['DELETE'])] + #[OA\Delete( + path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', + description: 'Deletes a single subscriber attribute.', + summary: 'Deletes an attribute.', + tags: ['subscriber-attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'definitionId', + description: 'attribute definition id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'subscriberId', + description: 'Subscriber id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success' + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 404, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse') + ) + ] + )] + public function delete( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?SubscriberAttributeDefinition $definition = null, + #[MapEntity(mapping: ['subscriberId' => 'id'])] ?Subscriber $subscriber = null, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$definition || !$subscriber) { + throw $this->createNotFoundException('Subscriber attribute not found.'); + } + $attribute = $this->attributeManager->getSubscriberAttribute($definition->getId(), $subscriber->getId()); + $this->attributeManager->delete($attribute); + + return $this->json(null, Response::HTTP_NO_CONTENT); + } + + #[Route('/{subscriberId}', name: 'get_subscriber_attributes', methods: ['GET'])] + #[OA\Get( + path: '/subscriber/attribute-values/{subscriberId}', + description: 'Returns a JSON list of all subscriber attributes.', + summary: 'Gets a list of all subscriber attributes.', + tags: ['subscriber-attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'subscriberId', + description: 'Subscriber id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'after_id', + description: 'Last id (starting from 0)', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 1, minimum: 1) + ), + new OA\Parameter( + name: 'limit', + description: 'Number of results per page', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 25, maximum: 100, minimum: 1) + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'items', + type: 'array', + items: new OA\Items(ref: '#/components/schemas/SubscriberAttributeValue') + ), + new OA\Property(property: 'pagination', ref: '#/components/schemas/CursorPagination') + ], + type: 'object' + ) + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ) + ] + )] + public function getPaginated(Request $request): JsonResponse + { + $this->requireAuthentication($request); + $filter = (new SubscriberAttributeValueFilter())->setSubscriberId($request->query->getInt('subscriberId')); + + return $this->json( + $this->paginatedDataProvider->getPaginatedList( + $request, + $this->normalizer, + SubscriberAttributeValue::class, + $filter + ), + Response::HTTP_OK + ); + } + + #[Route('/{subscriberId}/{definitionId}', name: 'get_subscriber_attribute', methods: ['GET'])] + #[OA\Get( + path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', + description: 'Returns a single attribute.', + summary: 'Gets subscriber attribute.', + tags: ['subscriber-attributes'], + parameters: [ + new OA\Parameter( + name: 'definitionId', + description: 'attribute definition id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'subscriberId', + description: 'Subscriber id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/SubscriberAttributeValue') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 404, + description: 'Failure', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'message', + type: 'string', + example: 'There is no attribute with that ID.' + ) + ], + type: 'object' + ) + ) + ] + )] + public function getAttributeDefinition( + Request $request, + #[MapEntity(mapping: ['subscriberId' => 'id'])] ?SubscriberAttributeDefinition $subscriber, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?SubscriberAttributeDefinition $definition, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$definition || !$subscriber) { + throw $this->createNotFoundException('Subscriber attribute not found.'); + } + $attribute = $this->attributeManager->getSubscriberAttribute($definition->getId(), $subscriber->getId()); + $this->attributeManager->delete($attribute); + + return $this->json( + $this->normalizer->normalize($attribute), + Response::HTTP_OK + ); + } +} diff --git a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php index 479e9a9..67d4d37 100644 --- a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php +++ b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php @@ -20,7 +20,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -#[Route('/attributes')] +#[Route('/subscriber/attributes')] class SubscriberAttributeDefinitionController extends BaseController { private AttributeDefinitionManager $definitionManager; @@ -42,7 +42,7 @@ public function __construct( #[Route('', name: 'create_attribute_definition', methods: ['POST'])] #[OA\Post( - path: '/attributes', + path: '/subscriber/attributes', description: 'Returns created subscriber attribute definition.', summary: 'Create a subscriber attribute definition.', requestBody: new OA\RequestBody( @@ -60,7 +60,7 @@ public function __construct( ] ) ), - tags: ['attributes'], + tags: ['subscriber-attributes'], parameters: [ new OA\Parameter( name: 'session', @@ -90,7 +90,7 @@ public function __construct( ), ] )] - public function create(Request $request,): JsonResponse + public function create(Request $request): JsonResponse { $this->requireAuthentication($request); @@ -105,7 +105,7 @@ public function create(Request $request,): JsonResponse #[Route('/{definitionId}', name: 'update_attribute_definition', methods: ['PUT'])] #[OA\Put( - path: '/attributes/{definitionId}', + path: '/subscriber/attributes/{definitionId}', description: 'Returns updated subscriber attribute definition.', summary: 'Update a subscriber attribute definition.', requestBody: new OA\RequestBody( @@ -123,7 +123,7 @@ public function create(Request $request,): JsonResponse ] ) ), - tags: ['attributes'], + tags: ['subscriber-attributes'], parameters: [ new OA\Parameter( name: 'definitionId', @@ -181,10 +181,10 @@ public function update( #[Route('/{definitionId}', name: 'delete_attribute_definition', methods: ['DELETE'])] #[OA\Delete( - path: '/attributes/{definitionId}', + path: '/subscriber/attributes/{definitionId}', description: 'Deletes a single subscriber attribute definition.', summary: 'Deletes an attribute definition.', - tags: ['attributes'], + tags: ['subscriber-attributes'], parameters: [ new OA\Parameter( name: 'session', @@ -234,10 +234,10 @@ public function delete( #[Route('', name: 'get_attribute_definitions', methods: ['GET'])] #[OA\Get( - path: '/attributes', + path: '/subscriber/attributes', description: 'Returns a JSON list of all subscriber attribute definitions.', summary: 'Gets a list of all subscriber attribute definitions.', - tags: ['attributes'], + tags: ['subscriber-attributes'], parameters: [ new OA\Parameter( name: 'session', @@ -286,7 +286,7 @@ public function delete( ) ] )] - public function getPaginated(Request $request,): JsonResponse + public function getPaginated(Request $request): JsonResponse { $this->requireAuthentication($request); @@ -302,10 +302,10 @@ public function getPaginated(Request $request,): JsonResponse #[Route('/{definitionId}', name: 'get_attribute_definition', methods: ['GET'])] #[OA\Get( - path: '/attributes/{definitionId}', + path: '/subscriber/attributes/{definitionId}', description: 'Returns a single attribute with specified ID.', summary: 'Gets attribute with specified ID.', - tags: ['attributes'], + tags: ['subscriber-attributes'], parameters: [ new OA\Parameter( name: 'definitionId', diff --git a/src/Subscription/OpenApi/SwaggerSchemasResponse.php b/src/Subscription/OpenApi/SwaggerSchemasResponse.php index 3d6fc24..638a70d 100644 --- a/src/Subscription/OpenApi/SwaggerSchemasResponse.php +++ b/src/Subscription/OpenApi/SwaggerSchemasResponse.php @@ -74,6 +74,14 @@ ], type: 'object' )] +#[OA\Schema( + schema: 'SubscriberAttributeValue', + properties: [ + new OA\Property(property: 'subscriber', ref: '#/components/schemas/Subscriber'), + new OA\Property(property: 'definition', ref: '#/components/schemas/AttributeDefinition'), + new OA\Property(property: 'value', type: 'string', example: 'United States'), + ], +)] class SwaggerSchemasResponse { } From 64b5c85553dd49a6f184998389473d4c5735e200 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Tue, 13 May 2025 20:40:26 +0400 Subject: [PATCH 08/21] ISSUE-345: tests folder restructure --- config/services/validators.yml | 10 +- src/Common/Validator/RequestValidator.php | 3 + .../Controller/AdministratorController.php | 2 +- .../Constraint/UniqueEmailValidator.php | 8 +- ...ubscriberAttributeDefinitionController.php | 2 +- ...=> SubscriberAttributeValueController.php} | 6 +- .../AbstractTestController.php | 6 +- .../EventListener/ExceptionListenerTest.php | 2 +- .../EventListener/ResponseListenerTest.php | 4 +- .../{ => Common}/Routing/RoutingTest.php | 2 +- .../AdministratorControllerTest.php | 119 ++++++++++++++ .../Controller/SessionControllerTest.php | 7 +- .../Fixtures}/Administrator.csv | 0 .../Fixtures}/AdministratorFixture.php | 2 +- .../Fixtures}/AdministratorToken.csv | 0 .../Fixtures}/AdministratorTokenFixture.php | 2 +- .../Controller/CampaignControllerTest.php | 9 +- .../Controller/TemplateControllerTest.php | 9 +- .../Fixtures}/Message.csv | 0 .../Fixtures}/MessageFixture.php | 2 +- .../Fixtures}/Template.csv | 0 .../Fixtures}/TemplateFixture.php | 2 +- .../Controller/ListControllerTest.php | 13 +- .../Controller/ListMembersControllerTest.php | 13 +- ...iberAttributeDefinitionControllerTest.php} | 25 +-- ...SubscriberAttributeValueControllerTest.php | 135 ++++++++++++++++ .../Controller/SubscriberControllerTest.php | 5 +- .../Controller/SubscriptionControllerTest.php | 13 +- .../Fixtures}/AttributeDefinitionFixture.php | 2 +- .../Fixtures}/Subscriber.csv | 0 .../Fixtures}/SubscriberFixture.php | 2 +- .../Fixtures}/SubscriberList.csv | 0 .../Fixtures}/SubscriberListFixture.php | 2 +- .../Fixtures}/Subscription.csv | 0 .../Fixtures}/SubscriptionFixture.php | 2 +- .../ViewHandler/SecuredViewHandlerTest.php | 2 +- .../CursorPaginationNormalizerTest.php | 2 +- .../PaginationCursorRequestFactoryTest.php | 2 +- .../Provider/PaginatedDataProviderTest.php | 2 +- .../Validator/RequestValidatorTest.php | 2 +- .../AdministratorNormalizerTest.php | 2 +- .../AdministratorTokenNormalizerTest.php | 2 +- .../Constraint/UniqueEmailValidatorTest.php | 4 +- .../UniqueLoginNameValidatorTest.php | 0 .../Serializer/MessageNormalizerTest.php | 2 +- .../TemplateImageNormalizerTest.php | 2 +- .../Serializer/TemplateNormalizerTest.php | 2 +- .../ContainsPlaceholderValidatorTest.php | 2 +- .../TemplateExistsValidatorTest.php | 2 +- .../AttributeDefinitionNormalizerTest.php | 2 +- .../SubscriberListNormalizerTest.php | 2 +- .../Serializer/SubscriberNormalizerTest.php | 2 +- .../Serializer/SubscriptionNormalizerTest.php | 2 +- .../Constraint/EmailExistsValidatorTest.php | 2 +- .../Constraint/UniqueEmailValidatorTest.php | 149 ++++++++++++++++++ 55 files changed, 501 insertions(+), 95 deletions(-) rename src/Subscription/Controller/{SubscriberAttributeController.php => SubscriberAttributeValueController.php} (98%) rename tests/Integration/{Controller => Common}/AbstractTestController.php (96%) rename tests/Integration/{ => Common}/EventListener/ExceptionListenerTest.php (97%) rename tests/Integration/{ => Common}/EventListener/ResponseListenerTest.php (94%) rename tests/Integration/{ => Common}/Routing/RoutingTest.php (88%) create mode 100644 tests/Integration/Identity/Controller/AdministratorControllerTest.php rename tests/Integration/{ => Identity}/Controller/SessionControllerTest.php (96%) rename tests/Integration/{Controller/Fixtures/Identity => Identity/Fixtures}/Administrator.csv (100%) rename tests/Integration/{Controller/Fixtures/Identity => Identity/Fixtures}/AdministratorFixture.php (95%) rename tests/Integration/{Controller/Fixtures/Identity => Identity/Fixtures}/AdministratorToken.csv (100%) rename tests/Integration/{Controller/Fixtures/Identity => Identity/Fixtures}/AdministratorTokenFixture.php (96%) rename tests/Integration/{ => Messaging}/Controller/CampaignControllerTest.php (87%) rename tests/Integration/{ => Messaging}/Controller/TemplateControllerTest.php (91%) rename tests/Integration/{Controller/Fixtures/Messaging => Messaging/Fixtures}/Message.csv (100%) rename tests/Integration/{Controller/Fixtures/Messaging => Messaging/Fixtures}/MessageFixture.php (97%) rename tests/Integration/{Controller/Fixtures/Messaging => Messaging/Fixtures}/Template.csv (100%) rename tests/Integration/{Controller/Fixtures/Messaging => Messaging/Fixtures}/TemplateFixture.php (94%) rename tests/Integration/{ => Subscription}/Controller/ListControllerTest.php (95%) rename tests/Integration/{ => Subscription}/Controller/ListMembersControllerTest.php (84%) rename tests/Integration/{Controller/AttributeDefinitionControllerTest.php => Subscription/Controller/SubscriberAttributeDefinitionControllerTest.php} (73%) create mode 100644 tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php rename tests/Integration/{ => Subscription}/Controller/SubscriberControllerTest.php (95%) rename tests/Integration/{ => Subscription}/Controller/SubscriptionControllerTest.php (76%) rename tests/Integration/{Controller/Fixtures/Subscription => Subscription/Fixtures}/AttributeDefinitionFixture.php (90%) rename tests/Integration/{Controller/Fixtures/Subscription => Subscription/Fixtures}/Subscriber.csv (100%) rename tests/Integration/{Controller/Fixtures/Subscription => Subscription/Fixtures}/SubscriberFixture.php (95%) rename tests/Integration/{Controller/Fixtures/Messaging => Subscription/Fixtures}/SubscriberList.csv (100%) rename tests/Integration/{Controller/Fixtures/Messaging => Subscription/Fixtures}/SubscriberListFixture.php (96%) rename tests/Integration/{Controller/Fixtures/Subscription => Subscription/Fixtures}/Subscription.csv (100%) rename tests/Integration/{Controller/Fixtures/Subscription => Subscription/Fixtures}/SubscriptionFixture.php (95%) rename tests/Unit/{ => Common}/Serializer/CursorPaginationNormalizerTest.php (97%) rename tests/Unit/{ => Common}/Service/Factory/PaginationCursorRequestFactoryTest.php (95%) rename tests/Unit/{ => Common}/Service/Provider/PaginatedDataProviderTest.php (98%) rename tests/Unit/{ => Common}/Validator/RequestValidatorTest.php (98%) rename tests/Unit/{ => Identity}/Serializer/AdministratorNormalizerTest.php (96%) rename tests/Unit/{ => Identity}/Serializer/AdministratorTokenNormalizerTest.php (96%) rename tests/Unit/{Validator => Identity/Vallidator}/Constraint/UniqueEmailValidatorTest.php (98%) rename tests/Unit/{Validator => Identity/Vallidator}/Constraint/UniqueLoginNameValidatorTest.php (100%) rename tests/Unit/{ => Messaging}/Serializer/MessageNormalizerTest.php (98%) rename tests/Unit/{ => Messaging}/Serializer/TemplateImageNormalizerTest.php (97%) rename tests/Unit/{ => Messaging}/Serializer/TemplateNormalizerTest.php (98%) rename tests/Unit/{ => Messaging}/Validator/Constraint/ContainsPlaceholderValidatorTest.php (96%) rename tests/Unit/{ => Messaging}/Validator/Constraint/TemplateExistsValidatorTest.php (97%) rename tests/Unit/{ => Subscription}/Serializer/AttributeDefinitionNormalizerTest.php (96%) rename tests/Unit/{ => Subscription}/Serializer/SubscriberListNormalizerTest.php (96%) rename tests/Unit/{ => Subscription}/Serializer/SubscriberNormalizerTest.php (98%) rename tests/Unit/{ => Subscription}/Serializer/SubscriptionNormalizerTest.php (97%) rename tests/Unit/{ => Subscription}/Validator/Constraint/EmailExistsValidatorTest.php (97%) create mode 100644 tests/Unit/Subscription/Validator/Constraint/UniqueEmailValidatorTest.php diff --git a/config/services/validators.yml b/config/services/validators.yml index f94c47f..6b158f5 100644 --- a/config/services/validators.yml +++ b/config/services/validators.yml @@ -4,7 +4,7 @@ services: autoconfigure: true public: false - PhpList\RestBundle\Identity\Validator\UniqueEmailValidator: + PhpList\RestBundle\Identity\Validator\Constraint\UniqueEmailValidator: autowire: true autoconfigure: true tags: [ 'validator.constraint_validator' ] @@ -24,14 +24,6 @@ services: autoconfigure: true tags: [ 'validator.constraint_validator' ] - PhpList\Core\Domain\Service\Validator\TemplateLinkValidator: - autowire: true - autoconfigure: true - - PhpList\Core\Domain\Service\Validator\TemplateImageValidator: - autowire: true - autoconfigure: true - PhpList\RestBundle\Messaging\Validator\Constraint\ContainsPlaceholderValidator: tags: ['validator.constraint_validator'] diff --git a/src/Common/Validator/RequestValidator.php b/src/Common/Validator/RequestValidator.php index df0ae67..c0aed04 100644 --- a/src/Common/Validator/RequestValidator.php +++ b/src/Common/Validator/RequestValidator.php @@ -38,6 +38,9 @@ public function validate(Request $request, string $dtoClass): RequestInterface if (isset($routeParams['listId'])) { $routeParams['listId'] = (int) $routeParams['listId']; } + if (isset($routeParams['administratorId'])) { + $routeParams['administratorId'] = (int) $routeParams['administratorId']; + } $data = array_merge($routeParams, $body ?? []); diff --git a/src/Identity/Controller/AdministratorController.php b/src/Identity/Controller/AdministratorController.php index 9afedd5..e99b5e4 100644 --- a/src/Identity/Controller/AdministratorController.php +++ b/src/Identity/Controller/AdministratorController.php @@ -230,7 +230,7 @@ public function updateAdministrator( $updateRequest = $this->validator->validate($request, UpdateAdministratorRequest::class); $this->administratorManager->updateAdministrator($administrator, $updateRequest->getDto()); - return $this->json(null, Response::HTTP_OK); + return $this->json($this->normalizer->normalize($administrator), Response::HTTP_OK); } #[Route('/{administratorId}', name: 'delete_administrator', methods: ['DELETE'])] diff --git a/src/Identity/Validator/Constraint/UniqueEmailValidator.php b/src/Identity/Validator/Constraint/UniqueEmailValidator.php index 0f0aa24..404ce13 100644 --- a/src/Identity/Validator/Constraint/UniqueEmailValidator.php +++ b/src/Identity/Validator/Constraint/UniqueEmailValidator.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Identity\Validator\Constraint; -use Doctrine\ORM\EntityManagerInterface; +use PhpList\Core\Domain\Identity\Repository\AdministratorRepository; use Symfony\Component\HttpKernel\Exception\ConflictHttpException; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; @@ -13,7 +13,7 @@ class UniqueEmailValidator extends ConstraintValidator { - public function __construct(private readonly EntityManagerInterface $entityManager) + public function __construct(private readonly AdministratorRepository $repository) { } @@ -31,9 +31,7 @@ public function validate($value, Constraint $constraint): void throw new UnexpectedValueException($value, 'string'); } - $existingUser = $this->entityManager - ->getRepository($constraint->entityClass) - ->findOneBy(['email' => $value]); + $existingUser = $this->repository->findOneBy(['email' => $value]); $dto = $this->context->getObject(); $updatingId = $dto->administratorId ?? null; diff --git a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php index 67d4d37..4dff070 100644 --- a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php +++ b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php @@ -20,7 +20,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -#[Route('/subscriber/attributes')] +#[Route('/subscribers/attributes')] class SubscriberAttributeDefinitionController extends BaseController { private AttributeDefinitionManager $definitionManager; diff --git a/src/Subscription/Controller/SubscriberAttributeController.php b/src/Subscription/Controller/SubscriberAttributeValueController.php similarity index 98% rename from src/Subscription/Controller/SubscriberAttributeController.php rename to src/Subscription/Controller/SubscriberAttributeValueController.php index df7ad2b..4357792 100644 --- a/src/Subscription/Controller/SubscriberAttributeController.php +++ b/src/Subscription/Controller/SubscriberAttributeValueController.php @@ -21,8 +21,8 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -#[Route('/subscriber/attribute-values')] -class SubscriberAttributeController extends BaseController +#[Route('/subscribers/attribute-values')] +class SubscriberAttributeValueController extends BaseController { private SubscriberAttributeManager $attributeManager; private SubscriberAttributeValueNormalizer $normalizer; @@ -114,7 +114,7 @@ public function createOrUpdate( $attributeDefinition = $this->attributeManager->createOrUpdate( subscriber:$subscriber, definition: $definition, - value: $request->get('value') + value: $request->toArray()['value'] ?? null ); $json = $this->normalizer->normalize($attributeDefinition, 'json'); diff --git a/tests/Integration/Controller/AbstractTestController.php b/tests/Integration/Common/AbstractTestController.php similarity index 96% rename from tests/Integration/Controller/AbstractTestController.php rename to tests/Integration/Common/AbstractTestController.php index 57d2a47..e431abb 100644 --- a/tests/Integration/Controller/AbstractTestController.php +++ b/tests/Integration/Common/AbstractTestController.php @@ -2,12 +2,12 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller; +namespace PhpList\RestBundle\Tests\Integration\Common; use Doctrine\ORM\Tools\SchemaTool; use PhpList\Core\TestingSupport\Traits\DatabaseTestTrait; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\HttpFoundation\Response; diff --git a/tests/Integration/EventListener/ExceptionListenerTest.php b/tests/Integration/Common/EventListener/ExceptionListenerTest.php similarity index 97% rename from tests/Integration/EventListener/ExceptionListenerTest.php rename to tests/Integration/Common/EventListener/ExceptionListenerTest.php index be1d7d9..9b6946d 100644 --- a/tests/Integration/EventListener/ExceptionListenerTest.php +++ b/tests/Integration/Common/EventListener/ExceptionListenerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\EventListener; +namespace PhpList\RestBundle\Tests\Integration\Common\EventListener; use PhpList\Core\Domain\Subscription\Exception\SubscriptionCreationException; use PhpList\RestBundle\Common\EventListener\ExceptionListener; diff --git a/tests/Integration/EventListener/ResponseListenerTest.php b/tests/Integration/Common/EventListener/ResponseListenerTest.php similarity index 94% rename from tests/Integration/EventListener/ResponseListenerTest.php rename to tests/Integration/Common/EventListener/ResponseListenerTest.php index a037b09..c1c2d84 100644 --- a/tests/Integration/EventListener/ResponseListenerTest.php +++ b/tests/Integration/Common/EventListener/ResponseListenerTest.php @@ -1,6 +1,8 @@ administratorRepository = self::getContainer()->get(AdministratorRepository::class); + } + + public function testControllerIsAvailableViaContainer(): void + { + self::assertInstanceOf( + AdministratorController::class, + self::getContainer()->get(AdministratorController::class) + ); + } + + public function testGetAdministratorsReturnsOk(): void + { + $this->loadFixtures([AdministratorFixture::class]); + $this->authenticatedJsonRequest('get', '/api/v2/administrators'); + + $this->assertHttpOkay(); + $data = $this->getDecodedJsonResponseContent(); + self::assertArrayHasKey('items', $data); + self::assertArrayHasKey('pagination', $data); + } + + public function testGetAdministratorReturnsData(): void + { + $this->loadFixtures([AdministratorFixture::class]); + $this->authenticatedJsonRequest('get', '/api/v2/administrators/1'); + + $this->assertHttpOkay(); + $data = $this->getDecodedJsonResponseContent(); + self::assertSame('john.doe', $data['login_name']); + } + + public function testGetAdministratorNotFound(): void + { + $this->authenticatedJsonRequest('get', '/api/v2/administrators/999'); + $this->assertHttpNotFound(); + } + + public function testCreateAdministratorWithValidDataReturnsCreated(): void + { + $this->authenticatedJsonRequest('post', '/api/v2/administrators', [], [], [], json_encode([ + 'loginName' => 'new.admin', + 'password' => 'NewPassword123!', + 'email' => 'new.admin@example.com', + ])); + + $this->assertHttpCreated(); + $data = $this->getDecodedJsonResponseContent(); + self::assertSame('new.admin', $data['login_name']); + } + + public function testUpdateAdministratorReturnsOk(): void + { + $this->loadFixtures([AdministratorFixture::class]); + + $this->authenticatedJsonRequest('put', '/api/v2/administrators/1', [], [], [], json_encode([ + 'email' => 'updated@example.com', + ])); + + $this->assertHttpOkay(); + $data = $this->getDecodedJsonResponseContent(); + self::assertSame('updated@example.com', $data['email']); + } + + public function testDeleteAdministratorReturnsNoContent(): void + { + $this->loadFixtures([AdministratorFixture::class]); + + $this->authenticatedJsonRequest('delete', '/api/v2/administrators/1'); + $this->assertHttpNoContent(); + + self::assertNull($this->administratorRepository->find(1)); + } + + public function testDeleteAdministratorNotFound(): void + { + $this->authenticatedJsonRequest('delete', '/api/v2/administrators/99999'); + $this->assertHttpNotFound(); + } + + public function testCreateAdministratorWithInvalidJsonReturns400(): void + { + $this->authenticatedJsonRequest('post', '/api/v2/administrators', [], [], [], 'not json'); + $this->assertHttpBadRequest(); + } + + public function testCreateAdministratorWithMissingFieldsReturns422(): void + { + $this->authenticatedJsonRequest('post', '/api/v2/administrators', [], [], [], json_encode([])); + $this->assertHttpUnprocessableEntity(); + } + + public function testPutAdministratorWithInvalidIdReturns404(): void + { + $this->authenticatedJsonRequest('put', '/api/v2/administrators/9999', [], [], [], json_encode([ + 'email' => 'example@example.com' + ])); + + $this->assertHttpNotFound(); + } +} diff --git a/tests/Integration/Controller/SessionControllerTest.php b/tests/Integration/Identity/Controller/SessionControllerTest.php similarity index 96% rename from tests/Integration/Controller/SessionControllerTest.php rename to tests/Integration/Identity/Controller/SessionControllerTest.php index 3121514..5f8b2b0 100644 --- a/tests/Integration/Controller/SessionControllerTest.php +++ b/tests/Integration/Identity/Controller/SessionControllerTest.php @@ -2,14 +2,15 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller; +namespace PhpList\RestBundle\Tests\Integration\Identity\Controller; use DateTime; use PhpList\Core\Domain\Identity\Model\AdministratorToken; use PhpList\Core\Domain\Identity\Repository\AdministratorTokenRepository; use PhpList\RestBundle\Identity\Controller\SessionController; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; +use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; /** * Testcase. diff --git a/tests/Integration/Controller/Fixtures/Identity/Administrator.csv b/tests/Integration/Identity/Fixtures/Administrator.csv similarity index 100% rename from tests/Integration/Controller/Fixtures/Identity/Administrator.csv rename to tests/Integration/Identity/Fixtures/Administrator.csv diff --git a/tests/Integration/Controller/Fixtures/Identity/AdministratorFixture.php b/tests/Integration/Identity/Fixtures/AdministratorFixture.php similarity index 95% rename from tests/Integration/Controller/Fixtures/Identity/AdministratorFixture.php rename to tests/Integration/Identity/Fixtures/AdministratorFixture.php index 9eac66b..16be665 100644 --- a/tests/Integration/Controller/Fixtures/Identity/AdministratorFixture.php +++ b/tests/Integration/Identity/Fixtures/AdministratorFixture.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity; +namespace PhpList\RestBundle\Tests\Integration\Identity\Fixtures; use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; diff --git a/tests/Integration/Controller/Fixtures/Identity/AdministratorToken.csv b/tests/Integration/Identity/Fixtures/AdministratorToken.csv similarity index 100% rename from tests/Integration/Controller/Fixtures/Identity/AdministratorToken.csv rename to tests/Integration/Identity/Fixtures/AdministratorToken.csv diff --git a/tests/Integration/Controller/Fixtures/Identity/AdministratorTokenFixture.php b/tests/Integration/Identity/Fixtures/AdministratorTokenFixture.php similarity index 96% rename from tests/Integration/Controller/Fixtures/Identity/AdministratorTokenFixture.php rename to tests/Integration/Identity/Fixtures/AdministratorTokenFixture.php index bac1160..3a47138 100644 --- a/tests/Integration/Controller/Fixtures/Identity/AdministratorTokenFixture.php +++ b/tests/Integration/Identity/Fixtures/AdministratorTokenFixture.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity; +namespace PhpList\RestBundle\Tests\Integration\Identity\Fixtures; use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; diff --git a/tests/Integration/Controller/CampaignControllerTest.php b/tests/Integration/Messaging/Controller/CampaignControllerTest.php similarity index 87% rename from tests/Integration/Controller/CampaignControllerTest.php rename to tests/Integration/Messaging/Controller/CampaignControllerTest.php index c63d41f..301dee3 100644 --- a/tests/Integration/Controller/CampaignControllerTest.php +++ b/tests/Integration/Messaging/Controller/CampaignControllerTest.php @@ -2,12 +2,13 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller; +namespace PhpList\RestBundle\Tests\Integration\Messaging\Controller; use PhpList\RestBundle\Messaging\Controller\CampaignController; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\MessageFixture; +use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; +use PhpList\RestBundle\Tests\Integration\Messaging\Fixtures\MessageFixture; class CampaignControllerTest extends AbstractTestController { diff --git a/tests/Integration/Controller/TemplateControllerTest.php b/tests/Integration/Messaging/Controller/TemplateControllerTest.php similarity index 91% rename from tests/Integration/Controller/TemplateControllerTest.php rename to tests/Integration/Messaging/Controller/TemplateControllerTest.php index 53313a7..8dd73cc 100644 --- a/tests/Integration/Controller/TemplateControllerTest.php +++ b/tests/Integration/Messaging/Controller/TemplateControllerTest.php @@ -2,13 +2,14 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller; +namespace PhpList\RestBundle\Tests\Integration\Messaging\Controller; use PhpList\Core\Domain\Messaging\Repository\TemplateRepository; use PhpList\RestBundle\Messaging\Controller\TemplateController; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\TemplateFixture; +use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; +use PhpList\RestBundle\Tests\Integration\Messaging\Fixtures\TemplateFixture; class TemplateControllerTest extends AbstractTestController { diff --git a/tests/Integration/Controller/Fixtures/Messaging/Message.csv b/tests/Integration/Messaging/Fixtures/Message.csv similarity index 100% rename from tests/Integration/Controller/Fixtures/Messaging/Message.csv rename to tests/Integration/Messaging/Fixtures/Message.csv diff --git a/tests/Integration/Controller/Fixtures/Messaging/MessageFixture.php b/tests/Integration/Messaging/Fixtures/MessageFixture.php similarity index 97% rename from tests/Integration/Controller/Fixtures/Messaging/MessageFixture.php rename to tests/Integration/Messaging/Fixtures/MessageFixture.php index 7135aa2..3e6b4a9 100644 --- a/tests/Integration/Controller/Fixtures/Messaging/MessageFixture.php +++ b/tests/Integration/Messaging/Fixtures/MessageFixture.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging; +namespace PhpList\RestBundle\Tests\Integration\Messaging\Fixtures; use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; diff --git a/tests/Integration/Controller/Fixtures/Messaging/Template.csv b/tests/Integration/Messaging/Fixtures/Template.csv similarity index 100% rename from tests/Integration/Controller/Fixtures/Messaging/Template.csv rename to tests/Integration/Messaging/Fixtures/Template.csv diff --git a/tests/Integration/Controller/Fixtures/Messaging/TemplateFixture.php b/tests/Integration/Messaging/Fixtures/TemplateFixture.php similarity index 94% rename from tests/Integration/Controller/Fixtures/Messaging/TemplateFixture.php rename to tests/Integration/Messaging/Fixtures/TemplateFixture.php index 5042f3c..9f26d96 100644 --- a/tests/Integration/Controller/Fixtures/Messaging/TemplateFixture.php +++ b/tests/Integration/Messaging/Fixtures/TemplateFixture.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging; +namespace PhpList\RestBundle\Tests\Integration\Messaging\Fixtures; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; diff --git a/tests/Integration/Controller/ListControllerTest.php b/tests/Integration/Subscription/Controller/ListControllerTest.php similarity index 95% rename from tests/Integration/Controller/ListControllerTest.php rename to tests/Integration/Subscription/Controller/ListControllerTest.php index f254831..3e4a835 100644 --- a/tests/Integration/Controller/ListControllerTest.php +++ b/tests/Integration/Subscription/Controller/ListControllerTest.php @@ -2,15 +2,16 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller; +namespace PhpList\RestBundle\Tests\Integration\Subscription\Controller; use PhpList\Core\Domain\Subscription\Repository\SubscriberListRepository; use PhpList\RestBundle\Subscription\Controller\ListController; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\SubscriberListFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\SubscriberFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\SubscriptionFixture; +use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberListFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriptionFixture; /** * Testcase. diff --git a/tests/Integration/Controller/ListMembersControllerTest.php b/tests/Integration/Subscription/Controller/ListMembersControllerTest.php similarity index 84% rename from tests/Integration/Controller/ListMembersControllerTest.php rename to tests/Integration/Subscription/Controller/ListMembersControllerTest.php index d40ae2a..5f3b93c 100644 --- a/tests/Integration/Controller/ListMembersControllerTest.php +++ b/tests/Integration/Subscription/Controller/ListMembersControllerTest.php @@ -2,14 +2,15 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller; +namespace PhpList\RestBundle\Tests\Integration\Subscription\Controller; use PhpList\RestBundle\Subscription\Controller\ListMembersController; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\SubscriberListFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\SubscriberFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\SubscriptionFixture; +use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberListFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriptionFixture; class ListMembersControllerTest extends AbstractTestController { diff --git a/tests/Integration/Controller/AttributeDefinitionControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberAttributeDefinitionControllerTest.php similarity index 73% rename from tests/Integration/Controller/AttributeDefinitionControllerTest.php rename to tests/Integration/Subscription/Controller/SubscriberAttributeDefinitionControllerTest.php index d680e36..b5adef0 100644 --- a/tests/Integration/Controller/AttributeDefinitionControllerTest.php +++ b/tests/Integration/Subscription/Controller/SubscriberAttributeDefinitionControllerTest.php @@ -2,15 +2,16 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller; +namespace PhpList\RestBundle\Tests\Integration\Subscription\Controller; use PhpList\Core\Domain\Subscription\Repository\SubscriberAttributeDefinitionRepository; use PhpList\RestBundle\Subscription\Controller\SubscriberAttributeDefinitionController; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\AttributeDefinitionFixture; +use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\AttributeDefinitionFixture; -class AttributeDefinitionControllerTest extends AbstractTestController +class SubscriberAttributeDefinitionControllerTest extends AbstractTestController { public function testControllerIsAvailableViaContainer() { @@ -22,21 +23,21 @@ public function testControllerIsAvailableViaContainer() public function testGetAttributesWithoutSessionKeyReturnsForbidden() { - self::getClient()->request('GET', '/api/v2/attributes'); + self::getClient()->request('GET', '/api/v2/subscribers/attributes'); $this->assertHttpForbidden(); } public function testGetAttributesWithSessionKeyReturnsOk() { $this->loadFixtures([AdministratorFixture::class, AdministratorTokenFixture::class]); - $this->authenticatedJsonRequest('GET', '/api/v2/attributes'); + $this->authenticatedJsonRequest('GET', '/api/v2/subscribers/attributes'); $this->assertHttpOkay(); } public function testGetAttributeWithInvalidIdReturnsNotFound() { $this->loadFixtures([AdministratorFixture::class, AdministratorTokenFixture::class]); - $this->authenticatedJsonRequest('GET', '/api/v2/attributes/999'); + $this->authenticatedJsonRequest('GET', '/api/v2/subscribers/attributes/999'); $this->assertHttpNotFound(); } @@ -53,7 +54,7 @@ public function testCreateAttributeDefinition() 'table_name' => 'list_attributes', ]); - $this->authenticatedJsonRequest('POST', '/api/v2/attributes', [], [], [], $payload); + $this->authenticatedJsonRequest('POST', '/api/v2/subscribers/attributes', [], [], [], $payload); $this->assertHttpCreated(); @@ -78,7 +79,7 @@ public function testUpdateAttributeDefinition() 'table_name' => 'list_attributes', ]); - $this->authenticatedJsonRequest('PUT', '/api/v2/attributes/1', [], [], [], $payload); + $this->authenticatedJsonRequest('PUT', '/api/v2/subscribers/attributes/1', [], [], [], $payload); $this->assertHttpOkay(); $response = $this->getDecodedJsonResponseContent(); self::assertSame('Updated Country', $response['name']); @@ -92,7 +93,7 @@ public function testDeleteAttributeDefinition() AttributeDefinitionFixture::class, ]); - $this->authenticatedJsonRequest('DELETE', '/api/v2/attributes/1'); + $this->authenticatedJsonRequest('DELETE', '/api/v2/subscribers/attributes/1'); $this->assertHttpNoContent(); $repo = self::getContainer()->get(SubscriberAttributeDefinitionRepository::class); @@ -109,7 +110,7 @@ public function testCreateAttributeDefinitionMissingNameReturnsValidationError() 'required' => false ]); - $this->authenticatedJsonRequest('POST', '/api/v2/attributes', [], [], [], $payload); + $this->authenticatedJsonRequest('POST', '/api/v2/subscribers/attributes', [], [], [], $payload); $this->assertHttpUnprocessableEntity(); } } diff --git a/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php new file mode 100644 index 0000000..432f07e --- /dev/null +++ b/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php @@ -0,0 +1,135 @@ +attributeValueRepo = self::getContainer()->get(SubscriberAttributeValueRepository::class); + } + + public function testControllerIsAvailableViaContainer(): void + { + self::assertInstanceOf( + SubscriberAttributeValueController::class, + self::getContainer()->get(SubscriberAttributeValueController::class) + ); + } + + public function testCreateOrUpdateAttributeValue(): void + { + $this->loadFixtures([ + SubscriberFixture::class, + AttributeDefinitionFixture::class, + ]); + + $subscriberId = 1; + $definitionId = 1; + $json = json_encode(['value' => 'Test Country']); + + $this->authenticatedJsonRequest( + 'post', + "/api/v2/subscribers/attribute-values/$subscriberId/$definitionId", + [], + [], + [], + $json + ); + + $this->assertHttpCreated(); + $response = $this->getDecodedJsonResponseContent(); + self::assertSame('Test Country', $response['value']); + } + + public function testGetSubscriberAttributeValue(): void + { + $this->loadFixtures([ + SubscriberFixture::class, + AttributeDefinitionFixture::class, + ]); + + $this->authenticatedJsonRequest( + 'post', + '/api/v2/subscribers/attribute-values/1/1', + [], + [], + [], + json_encode(['value' => 'Test City']) + ); + + $this->assertHttpCreated(); + + $this->authenticatedJsonRequest( + 'get', + '/api/v2/subscribers/attribute-values/1/1' + ); + + $this->assertHttpOkay(); + $response = $this->getDecodedJsonResponseContent(); + self::assertSame('Test City', $response['value']); + } + + public function testDeleteAttributeValue(): void + { + $this->loadFixtures([ + SubscriberFixture::class, + AttributeDefinitionFixture::class, + ]); + + $this->authenticatedJsonRequest( + 'post', + '/api/v2/subscribers/attribute-values/1/1', + [], + [], + [], + json_encode(['value' => 'To Delete']) + ); + + $this->assertHttpCreated(); + + $this->authenticatedJsonRequest( + 'delete', + '/api/v2/subscribers/attribute-values/1/1' + ); + + $this->assertHttpNoContent(); + } + + public function testGetPaginatedAttributes(): void + { + $this->loadFixtures([ + SubscriberFixture::class, + AttributeDefinitionFixture::class, + ]); + + $this->authenticatedJsonRequest( + 'get', + '/api/v2/subscribers/attribute-values/1' + ); + + $this->assertHttpOkay(); + $response = $this->getDecodedJsonResponseContent(); + self::assertArrayHasKey('items', $response); + self::assertArrayHasKey('pagination', $response); + } + + public function testAttributeValueNotFoundReturns404(): void + { + $this->authenticatedJsonRequest( + 'get', + '/api/v2/subscribers/attribute-values/999/999' + ); + + $this->assertHttpNotFound(); + } +} diff --git a/tests/Integration/Controller/SubscriberControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberControllerTest.php similarity index 95% rename from tests/Integration/Controller/SubscriberControllerTest.php rename to tests/Integration/Subscription/Controller/SubscriberControllerTest.php index 380fa9e..240cfa9 100644 --- a/tests/Integration/Controller/SubscriberControllerTest.php +++ b/tests/Integration/Subscription/Controller/SubscriberControllerTest.php @@ -2,12 +2,13 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller; +namespace PhpList\RestBundle\Tests\Integration\Subscription\Controller; use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository; use PhpList\RestBundle\Subscription\Controller\SubscriberController; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\SubscriberFixture; +use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberFixture; /** * Testcase. diff --git a/tests/Integration/Controller/SubscriptionControllerTest.php b/tests/Integration/Subscription/Controller/SubscriptionControllerTest.php similarity index 76% rename from tests/Integration/Controller/SubscriptionControllerTest.php rename to tests/Integration/Subscription/Controller/SubscriptionControllerTest.php index e985134..a50ec84 100644 --- a/tests/Integration/Controller/SubscriptionControllerTest.php +++ b/tests/Integration/Subscription/Controller/SubscriptionControllerTest.php @@ -2,14 +2,15 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller; +namespace PhpList\RestBundle\Tests\Integration\Subscription\Controller; use PhpList\RestBundle\Subscription\Controller\SubscriptionController; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Identity\AdministratorTokenFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging\SubscriberListFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\SubscriberFixture; -use PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription\SubscriptionFixture; +use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; +use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberListFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriptionFixture; class SubscriptionControllerTest extends AbstractTestController { diff --git a/tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php b/tests/Integration/Subscription/Fixtures/AttributeDefinitionFixture.php similarity index 90% rename from tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php rename to tests/Integration/Subscription/Fixtures/AttributeDefinitionFixture.php index 00eaa8c..8f971ec 100644 --- a/tests/Integration/Controller/Fixtures/Subscription/AttributeDefinitionFixture.php +++ b/tests/Integration/Subscription/Fixtures/AttributeDefinitionFixture.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription; +namespace PhpList\RestBundle\Tests\Integration\Subscription\Fixtures; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Common\DataFixtures\FixtureInterface; diff --git a/tests/Integration/Controller/Fixtures/Subscription/Subscriber.csv b/tests/Integration/Subscription/Fixtures/Subscriber.csv similarity index 100% rename from tests/Integration/Controller/Fixtures/Subscription/Subscriber.csv rename to tests/Integration/Subscription/Fixtures/Subscriber.csv diff --git a/tests/Integration/Controller/Fixtures/Subscription/SubscriberFixture.php b/tests/Integration/Subscription/Fixtures/SubscriberFixture.php similarity index 95% rename from tests/Integration/Controller/Fixtures/Subscription/SubscriberFixture.php rename to tests/Integration/Subscription/Fixtures/SubscriberFixture.php index b8882db..6b49d0f 100644 --- a/tests/Integration/Controller/Fixtures/Subscription/SubscriberFixture.php +++ b/tests/Integration/Subscription/Fixtures/SubscriberFixture.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription; +namespace PhpList\RestBundle\Tests\Integration\Subscription\Fixtures; use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; diff --git a/tests/Integration/Controller/Fixtures/Messaging/SubscriberList.csv b/tests/Integration/Subscription/Fixtures/SubscriberList.csv similarity index 100% rename from tests/Integration/Controller/Fixtures/Messaging/SubscriberList.csv rename to tests/Integration/Subscription/Fixtures/SubscriberList.csv diff --git a/tests/Integration/Controller/Fixtures/Messaging/SubscriberListFixture.php b/tests/Integration/Subscription/Fixtures/SubscriberListFixture.php similarity index 96% rename from tests/Integration/Controller/Fixtures/Messaging/SubscriberListFixture.php rename to tests/Integration/Subscription/Fixtures/SubscriberListFixture.php index 11ea246..c1c4d11 100644 --- a/tests/Integration/Controller/Fixtures/Messaging/SubscriberListFixture.php +++ b/tests/Integration/Subscription/Fixtures/SubscriberListFixture.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Messaging; +namespace PhpList\RestBundle\Tests\Integration\Subscription\Fixtures; use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; diff --git a/tests/Integration/Controller/Fixtures/Subscription/Subscription.csv b/tests/Integration/Subscription/Fixtures/Subscription.csv similarity index 100% rename from tests/Integration/Controller/Fixtures/Subscription/Subscription.csv rename to tests/Integration/Subscription/Fixtures/Subscription.csv diff --git a/tests/Integration/Controller/Fixtures/Subscription/SubscriptionFixture.php b/tests/Integration/Subscription/Fixtures/SubscriptionFixture.php similarity index 95% rename from tests/Integration/Controller/Fixtures/Subscription/SubscriptionFixture.php rename to tests/Integration/Subscription/Fixtures/SubscriptionFixture.php index e839894..8ff65f3 100644 --- a/tests/Integration/Controller/Fixtures/Subscription/SubscriptionFixture.php +++ b/tests/Integration/Subscription/Fixtures/SubscriptionFixture.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Integration\Controller\Fixtures\Subscription; +namespace PhpList\RestBundle\Tests\Integration\Subscription\Fixtures; use DateTime; use Doctrine\Bundle\FixturesBundle\Fixture; diff --git a/tests/Integration/ViewHandler/SecuredViewHandlerTest.php b/tests/Integration/ViewHandler/SecuredViewHandlerTest.php index fbe0cc7..0b3c0cc 100644 --- a/tests/Integration/ViewHandler/SecuredViewHandlerTest.php +++ b/tests/Integration/ViewHandler/SecuredViewHandlerTest.php @@ -4,7 +4,7 @@ namespace PhpList\RestBundle\Tests\Integration\ViewHandler; -use PhpList\RestBundle\Tests\Integration\Controller\AbstractTestController; +use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; /** * Test for security headers diff --git a/tests/Unit/Serializer/CursorPaginationNormalizerTest.php b/tests/Unit/Common/Serializer/CursorPaginationNormalizerTest.php similarity index 97% rename from tests/Unit/Serializer/CursorPaginationNormalizerTest.php rename to tests/Unit/Common/Serializer/CursorPaginationNormalizerTest.php index 45a8419..61495c6 100644 --- a/tests/Unit/Serializer/CursorPaginationNormalizerTest.php +++ b/tests/Unit/Common/Serializer/CursorPaginationNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Common\Serializer; use PhpList\RestBundle\Common\Dto\CursorPaginationResult; use PhpList\RestBundle\Common\Serializer\CursorPaginationNormalizer; diff --git a/tests/Unit/Service/Factory/PaginationCursorRequestFactoryTest.php b/tests/Unit/Common/Service/Factory/PaginationCursorRequestFactoryTest.php similarity index 95% rename from tests/Unit/Service/Factory/PaginationCursorRequestFactoryTest.php rename to tests/Unit/Common/Service/Factory/PaginationCursorRequestFactoryTest.php index f1a37de..cc969b6 100644 --- a/tests/Unit/Service/Factory/PaginationCursorRequestFactoryTest.php +++ b/tests/Unit/Common/Service/Factory/PaginationCursorRequestFactoryTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Service\Factory; +namespace PhpList\RestBundle\Tests\Unit\Common\Service\Factory; use PhpList\RestBundle\Common\Service\Factory\PaginationCursorRequestFactory; use PHPUnit\Framework\TestCase; diff --git a/tests/Unit/Service/Provider/PaginatedDataProviderTest.php b/tests/Unit/Common/Service/Provider/PaginatedDataProviderTest.php similarity index 98% rename from tests/Unit/Service/Provider/PaginatedDataProviderTest.php rename to tests/Unit/Common/Service/Provider/PaginatedDataProviderTest.php index 01f33ca..ed05555 100644 --- a/tests/Unit/Service/Provider/PaginatedDataProviderTest.php +++ b/tests/Unit/Common/Service/Provider/PaginatedDataProviderTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Service\Provider; +namespace PhpList\RestBundle\Tests\Unit\Common\Service\Provider; use Doctrine\ORM\EntityManagerInterface; use PhpList\RestBundle\Common\Dto\CursorPaginationResult; diff --git a/tests/Unit/Validator/RequestValidatorTest.php b/tests/Unit/Common/Validator/RequestValidatorTest.php similarity index 98% rename from tests/Unit/Validator/RequestValidatorTest.php rename to tests/Unit/Common/Validator/RequestValidatorTest.php index ad93006..ce9adef 100644 --- a/tests/Unit/Validator/RequestValidatorTest.php +++ b/tests/Unit/Common/Validator/RequestValidatorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Validator; +namespace PhpList\RestBundle\Tests\Unit\Common\Validator; use PhpList\RestBundle\Common\Request\RequestInterface; use PhpList\RestBundle\Common\Validator\RequestValidator; diff --git a/tests/Unit/Serializer/AdministratorNormalizerTest.php b/tests/Unit/Identity/Serializer/AdministratorNormalizerTest.php similarity index 96% rename from tests/Unit/Serializer/AdministratorNormalizerTest.php rename to tests/Unit/Identity/Serializer/AdministratorNormalizerTest.php index efe89bc..0fa075a 100644 --- a/tests/Unit/Serializer/AdministratorNormalizerTest.php +++ b/tests/Unit/Identity/Serializer/AdministratorNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Identity\Serializer; use DateTime; use InvalidArgumentException; diff --git a/tests/Unit/Serializer/AdministratorTokenNormalizerTest.php b/tests/Unit/Identity/Serializer/AdministratorTokenNormalizerTest.php similarity index 96% rename from tests/Unit/Serializer/AdministratorTokenNormalizerTest.php rename to tests/Unit/Identity/Serializer/AdministratorTokenNormalizerTest.php index 788e7cd..1a9b534 100644 --- a/tests/Unit/Serializer/AdministratorTokenNormalizerTest.php +++ b/tests/Unit/Identity/Serializer/AdministratorTokenNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Identity\Serializer; use DateTime; use PhpList\Core\Domain\Identity\Model\AdministratorToken; diff --git a/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php b/tests/Unit/Identity/Vallidator/Constraint/UniqueEmailValidatorTest.php similarity index 98% rename from tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php rename to tests/Unit/Identity/Vallidator/Constraint/UniqueEmailValidatorTest.php index 8a1fa10..5f0ab2f 100644 --- a/tests/Unit/Validator/Constraint/UniqueEmailValidatorTest.php +++ b/tests/Unit/Identity/Vallidator/Constraint/UniqueEmailValidatorTest.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Validator\Constraint; +namespace PhpList\RestBundle\Tests\Unit\Identity\Vallidator\Constraint; use Doctrine\ORM\EntityManagerInterface; use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository; -use PhpList\RestBundle\Subscription\Validator\Constraint\UniqueEmailValidator; use PhpList\RestBundle\Subscription\Validator\Constraint\UniqueEmail; +use PhpList\RestBundle\Subscription\Validator\Constraint\UniqueEmailValidator; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpKernel\Exception\ConflictHttpException; diff --git a/tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php b/tests/Unit/Identity/Vallidator/Constraint/UniqueLoginNameValidatorTest.php similarity index 100% rename from tests/Unit/Validator/Constraint/UniqueLoginNameValidatorTest.php rename to tests/Unit/Identity/Vallidator/Constraint/UniqueLoginNameValidatorTest.php diff --git a/tests/Unit/Serializer/MessageNormalizerTest.php b/tests/Unit/Messaging/Serializer/MessageNormalizerTest.php similarity index 98% rename from tests/Unit/Serializer/MessageNormalizerTest.php rename to tests/Unit/Messaging/Serializer/MessageNormalizerTest.php index 90e255f..df5a33d 100644 --- a/tests/Unit/Serializer/MessageNormalizerTest.php +++ b/tests/Unit/Messaging/Serializer/MessageNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Messaging\Serializer; use DateTime; use PhpList\Core\Domain\Messaging\Model\Message; diff --git a/tests/Unit/Serializer/TemplateImageNormalizerTest.php b/tests/Unit/Messaging/Serializer/TemplateImageNormalizerTest.php similarity index 97% rename from tests/Unit/Serializer/TemplateImageNormalizerTest.php rename to tests/Unit/Messaging/Serializer/TemplateImageNormalizerTest.php index f6d7b6d..b5a4f57 100644 --- a/tests/Unit/Serializer/TemplateImageNormalizerTest.php +++ b/tests/Unit/Messaging/Serializer/TemplateImageNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Messaging\Serializer; use PhpList\Core\Domain\Messaging\Model\Template; use PhpList\Core\Domain\Messaging\Model\TemplateImage; diff --git a/tests/Unit/Serializer/TemplateNormalizerTest.php b/tests/Unit/Messaging/Serializer/TemplateNormalizerTest.php similarity index 98% rename from tests/Unit/Serializer/TemplateNormalizerTest.php rename to tests/Unit/Messaging/Serializer/TemplateNormalizerTest.php index 8770509..4ea3def 100644 --- a/tests/Unit/Serializer/TemplateNormalizerTest.php +++ b/tests/Unit/Messaging/Serializer/TemplateNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Messaging\Serializer; use Doctrine\Common\Collections\ArrayCollection; use PhpList\Core\Domain\Messaging\Model\Template; diff --git a/tests/Unit/Validator/Constraint/ContainsPlaceholderValidatorTest.php b/tests/Unit/Messaging/Validator/Constraint/ContainsPlaceholderValidatorTest.php similarity index 96% rename from tests/Unit/Validator/Constraint/ContainsPlaceholderValidatorTest.php rename to tests/Unit/Messaging/Validator/Constraint/ContainsPlaceholderValidatorTest.php index f2bef35..bdd2105 100644 --- a/tests/Unit/Validator/Constraint/ContainsPlaceholderValidatorTest.php +++ b/tests/Unit/Messaging/Validator/Constraint/ContainsPlaceholderValidatorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Validator\Constraint; +namespace PhpList\RestBundle\Tests\Unit\Messaging\Validator\Constraint; use PhpList\RestBundle\Messaging\Validator\Constraint\ContainsPlaceholder; use PhpList\RestBundle\Messaging\Validator\Constraint\ContainsPlaceholderValidator; diff --git a/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php b/tests/Unit/Messaging/Validator/Constraint/TemplateExistsValidatorTest.php similarity index 97% rename from tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php rename to tests/Unit/Messaging/Validator/Constraint/TemplateExistsValidatorTest.php index 566a781..8d6ca8f 100644 --- a/tests/Unit/Validator/Constraint/TemplateExistsValidatorTest.php +++ b/tests/Unit/Messaging/Validator/Constraint/TemplateExistsValidatorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Validator\Constraint; +namespace PhpList\RestBundle\Tests\Unit\Messaging\Validator\Constraint; use PhpList\Core\Domain\Messaging\Model\Template; use PhpList\Core\Domain\Messaging\Repository\TemplateRepository; diff --git a/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php b/tests/Unit/Subscription/Serializer/AttributeDefinitionNormalizerTest.php similarity index 96% rename from tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php rename to tests/Unit/Subscription/Serializer/AttributeDefinitionNormalizerTest.php index 58966d1..c2b8dc0 100644 --- a/tests/Unit/Serializer/AttributeDefinitionNormalizerTest.php +++ b/tests/Unit/Subscription/Serializer/AttributeDefinitionNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Subscription\Serializer; use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition; use PhpList\RestBundle\Subscription\Serializer\AttributeDefinitionNormalizer; diff --git a/tests/Unit/Serializer/SubscriberListNormalizerTest.php b/tests/Unit/Subscription/Serializer/SubscriberListNormalizerTest.php similarity index 96% rename from tests/Unit/Serializer/SubscriberListNormalizerTest.php rename to tests/Unit/Subscription/Serializer/SubscriberListNormalizerTest.php index f17a3d9..1eebf3f 100644 --- a/tests/Unit/Serializer/SubscriberListNormalizerTest.php +++ b/tests/Unit/Subscription/Serializer/SubscriberListNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Subscription\Serializer; use DateTime; use PhpList\Core\Domain\Subscription\Model\SubscriberList; diff --git a/tests/Unit/Serializer/SubscriberNormalizerTest.php b/tests/Unit/Subscription/Serializer/SubscriberNormalizerTest.php similarity index 98% rename from tests/Unit/Serializer/SubscriberNormalizerTest.php rename to tests/Unit/Subscription/Serializer/SubscriberNormalizerTest.php index e0e100b..8a87c7c 100644 --- a/tests/Unit/Serializer/SubscriberNormalizerTest.php +++ b/tests/Unit/Subscription/Serializer/SubscriberNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Subscription\Serializer; use DateTime; use Doctrine\Common\Collections\ArrayCollection; diff --git a/tests/Unit/Serializer/SubscriptionNormalizerTest.php b/tests/Unit/Subscription/Serializer/SubscriptionNormalizerTest.php similarity index 97% rename from tests/Unit/Serializer/SubscriptionNormalizerTest.php rename to tests/Unit/Subscription/Serializer/SubscriptionNormalizerTest.php index 91e8970..4e3b409 100644 --- a/tests/Unit/Serializer/SubscriptionNormalizerTest.php +++ b/tests/Unit/Subscription/Serializer/SubscriptionNormalizerTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Serializer; +namespace PhpList\RestBundle\Tests\Unit\Subscription\Serializer; use DateTime; use PhpList\Core\Domain\Subscription\Model\Subscriber; diff --git a/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php b/tests/Unit/Subscription/Validator/Constraint/EmailExistsValidatorTest.php similarity index 97% rename from tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php rename to tests/Unit/Subscription/Validator/Constraint/EmailExistsValidatorTest.php index bcaf532..9dccd7b 100644 --- a/tests/Unit/Validator/Constraint/EmailExistsValidatorTest.php +++ b/tests/Unit/Subscription/Validator/Constraint/EmailExistsValidatorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpList\RestBundle\Tests\Unit\Validator\Constraint; +namespace PhpList\RestBundle\Tests\Unit\Subscription\Validator\Constraint; use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Repository\SubscriberRepository; diff --git a/tests/Unit/Subscription/Validator/Constraint/UniqueEmailValidatorTest.php b/tests/Unit/Subscription/Validator/Constraint/UniqueEmailValidatorTest.php new file mode 100644 index 0000000..e32852d --- /dev/null +++ b/tests/Unit/Subscription/Validator/Constraint/UniqueEmailValidatorTest.php @@ -0,0 +1,149 @@ +entityManager = $this->createMock(EntityManagerInterface::class); + $this->validator = new UniqueEmailValidator($this->entityManager); + $this->context = $this->createMock(ExecutionContextInterface::class); + $this->validator->initialize($this->context); + } + + public function testThrowsUnexpectedTypeExceptionWhenConstraintIsWrong(): void + { + $this->expectException(UnexpectedTypeException::class); + $this->validator->validate('anything', $this->createMock(Constraint::class)); + } + + public function testSkipsValidationForNullOrEmpty(): void + { + $this->entityManager->expects(self::never())->method('getRepository'); + + $this->validator->validate(null, new UniqueEmail(Subscriber::class)); + $this->validator->validate('', new UniqueEmail(Subscriber::class)); + + $this->addToAssertionCount(1); + } + + public function testThrowsUnexpectedValueExceptionForNonString(): void + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate(123, new UniqueEmail(Subscriber::class)); + } + + public function testThrowsConflictHttpExceptionWhenEmailAlreadyExistsWithDifferentId(): void + { + $email = 'foo@bar.com'; + + $existingUser = $this->createConfiguredMock(Subscriber::class, [ + 'getId' => 99 + ]); + + $repo = $this->createMock(SubscriberRepository::class); + $repo->expects(self::once()) + ->method('findOneBy') + ->with(['email' => $email]) + ->willReturn($existingUser); + + $this->entityManager + ->expects(self::once()) + ->method('getRepository') + ->with(Subscriber::class) + ->willReturn($repo); + + $dto = new class { + public int $subscriberId = 100; + }; + + $this->context + ->method('getObject') + ->willReturn($dto); + + $this->expectException(ConflictHttpException::class); + $this->expectExceptionMessage('Email already exists.'); + + $this->validator->validate($email, new UniqueEmail(Subscriber::class)); + } + + public function testAllowsSameEmailForSameSubscriberId(): void + { + $email = 'foo@bar.com'; + + $existingUser = $this->createConfiguredMock(Subscriber::class, [ + 'getId' => 100 + ]); + + $repo = $this->createMock(SubscriberRepository::class); + $repo->expects(self::once()) + ->method('findOneBy') + ->with(['email' => $email]) + ->willReturn($existingUser); + + $this->entityManager + ->expects(self::once()) + ->method('getRepository') + ->with(Subscriber::class) + ->willReturn($repo); + + $dto = new class { + public int $subscriberId = 100; + }; + + $this->context + ->method('getObject') + ->willReturn($dto); + + $this->validator->validate($email, new UniqueEmail(Subscriber::class)); + + $this->addToAssertionCount(1); + } + + public function testAllowsUniqueEmailWhenNoExistingSubscriber(): void + { + $repo = $this->createMock(SubscriberRepository::class); + $repo->expects(self::once()) + ->method('findOneBy') + ->with(['email' => 'new@example.com']) + ->willReturn(null); + + $this->entityManager + ->expects(self::once()) + ->method('getRepository') + ->with(Subscriber::class) + ->willReturn($repo); + + $dto = new class { + public int $subscriberId = 200; + }; + + $this->context + ->method('getObject') + ->willReturn($dto); + + $this->validator->validate('new@example.com', new UniqueEmail(Subscriber::class)); + + $this->addToAssertionCount(1); + } +} From 2b47c39ec03fc44360efa8d0fcc7a91bb29299a3 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Thu, 15 May 2025 21:51:44 +0400 Subject: [PATCH 09/21] ISSUE-345: add tests --- .../SubscriberAttributeValueController.php | 18 ++-- ...oller.php => SubscriberListController.php} | 2 +- ...riberAttributeDefinitionControllerTest.php | 6 +- ...SubscriberAttributeValueControllerTest.php | 56 ++--------- ...t.php => SubscriberListControllerTest.php} | 9 +- ... SubscriberAttributeDefinitionFixture.php} | 2 +- .../SubscriberAttributeValueFixture.php | 36 +++++++ .../CreateAdministratorRequestTest.php | 42 ++++++++ .../Request/CreateSessionRequestTest.php | 24 +++++ .../UpdateAdministratorRequestTest.php | 44 +++++++++ .../Request/CreateMessageRequestTest.php | 92 ++++++++++++++++++ .../Request/CreateTemplateRequestTest.php | 80 ++++++++++++++++ .../Request/UpdateMessageRequestTest.php | 95 +++++++++++++++++++ .../CreateAttributeDefinitionRequestTest.php | 49 ++++++++++ .../CreateSubscriberListRequestTest.php | 43 +++++++++ .../Request/CreateSubscriberRequestTest.php | 40 ++++++++ .../Request/SubscriptionRequestTest.php | 32 +++++++ .../UpdateAttributeDefinitionRequestTest.php | 49 ++++++++++ .../Request/UpdateSubscriberRequestTest.php | 35 +++++++ 19 files changed, 690 insertions(+), 64 deletions(-) rename src/Subscription/Controller/{ListController.php => SubscriberListController.php} (99%) rename tests/Integration/Subscription/Controller/{ListControllerTest.php => SubscriberListControllerTest.php} (97%) rename tests/Integration/Subscription/Fixtures/{AttributeDefinitionFixture.php => SubscriberAttributeDefinitionFixture.php} (89%) create mode 100644 tests/Integration/Subscription/Fixtures/SubscriberAttributeValueFixture.php create mode 100644 tests/Unit/Identity/Request/CreateAdministratorRequestTest.php create mode 100644 tests/Unit/Identity/Request/CreateSessionRequestTest.php create mode 100644 tests/Unit/Identity/Request/UpdateAdministratorRequestTest.php create mode 100644 tests/Unit/Messaging/Request/CreateMessageRequestTest.php create mode 100644 tests/Unit/Messaging/Request/CreateTemplateRequestTest.php create mode 100644 tests/Unit/Messaging/Request/UpdateMessageRequestTest.php create mode 100644 tests/Unit/Subscription/Request/CreateAttributeDefinitionRequestTest.php create mode 100644 tests/Unit/Subscription/Request/CreateSubscriberListRequestTest.php create mode 100644 tests/Unit/Subscription/Request/CreateSubscriberRequestTest.php create mode 100644 tests/Unit/Subscription/Request/SubscriptionRequestTest.php create mode 100644 tests/Unit/Subscription/Request/UpdateAttributeDefinitionRequestTest.php create mode 100644 tests/Unit/Subscription/Request/UpdateSubscriberRequestTest.php diff --git a/src/Subscription/Controller/SubscriberAttributeValueController.php b/src/Subscription/Controller/SubscriberAttributeValueController.php index 4357792..1ad4ad5 100644 --- a/src/Subscription/Controller/SubscriberAttributeValueController.php +++ b/src/Subscription/Controller/SubscriberAttributeValueController.php @@ -176,15 +176,18 @@ public function delete( if (!$definition || !$subscriber) { throw $this->createNotFoundException('Subscriber attribute not found.'); } - $attribute = $this->attributeManager->getSubscriberAttribute($definition->getId(), $subscriber->getId()); + $attribute = $this->attributeManager->getSubscriberAttribute($subscriber->getId(), $definition->getId()); + if ($attribute === null) { + throw $this->createNotFoundException('Subscriber attribute not found.'); + } $this->attributeManager->delete($attribute); return $this->json(null, Response::HTTP_NO_CONTENT); } - #[Route('/{subscriberId}', name: 'get_subscriber_attributes', methods: ['GET'])] + #[Route('/{subscriberId}', name: 'get_subscriber_attribute_list', methods: ['GET'])] #[OA\Get( - path: '/subscriber/attribute-values/{subscriberId}', + path: '/subscribers/attribute-values/{subscriberId}', description: 'Returns a JSON list of all subscriber attributes.', summary: 'Gets a list of all subscriber attributes.', tags: ['subscriber-attributes'], @@ -241,10 +244,13 @@ public function delete( ) ] )] - public function getPaginated(Request $request): JsonResponse - { + public function getPaginated( + Request $request, + #[MapEntity(mapping: ['subscriberId' => 'id'])] ?Subscriber $subscriber = null, + ): JsonResponse { $this->requireAuthentication($request); - $filter = (new SubscriberAttributeValueFilter())->setSubscriberId($request->query->getInt('subscriberId')); + + $filter = (new SubscriberAttributeValueFilter())->setSubscriberId($subscriber->getId()); return $this->json( $this->paginatedDataProvider->getPaginatedList( diff --git a/src/Subscription/Controller/ListController.php b/src/Subscription/Controller/SubscriberListController.php similarity index 99% rename from src/Subscription/Controller/ListController.php rename to src/Subscription/Controller/SubscriberListController.php index 9b3e0a3..408e5df 100644 --- a/src/Subscription/Controller/ListController.php +++ b/src/Subscription/Controller/SubscriberListController.php @@ -27,7 +27,7 @@ * @author Tatevik Grigoryan */ #[Route('/lists')] -class ListController extends BaseController +class SubscriberListController extends BaseController { private SubscriberListNormalizer $normalizer; private SubscriberListManager $subscriberListManager; diff --git a/tests/Integration/Subscription/Controller/SubscriberAttributeDefinitionControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberAttributeDefinitionControllerTest.php index b5adef0..0768c81 100644 --- a/tests/Integration/Subscription/Controller/SubscriberAttributeDefinitionControllerTest.php +++ b/tests/Integration/Subscription/Controller/SubscriberAttributeDefinitionControllerTest.php @@ -9,7 +9,7 @@ use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; -use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\AttributeDefinitionFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberAttributeDefinitionFixture; class SubscriberAttributeDefinitionControllerTest extends AbstractTestController { @@ -67,7 +67,7 @@ public function testUpdateAttributeDefinition() $this->loadFixtures([ AdministratorFixture::class, AdministratorTokenFixture::class, - AttributeDefinitionFixture::class, + SubscriberAttributeDefinitionFixture::class, ]); $payload = json_encode([ @@ -90,7 +90,7 @@ public function testDeleteAttributeDefinition() $this->loadFixtures([ AdministratorFixture::class, AdministratorTokenFixture::class, - AttributeDefinitionFixture::class, + SubscriberAttributeDefinitionFixture::class, ]); $this->authenticatedJsonRequest('DELETE', '/api/v2/subscribers/attributes/1'); diff --git a/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php index 432f07e..4b1a84c 100644 --- a/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php +++ b/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php @@ -4,19 +4,14 @@ namespace PhpList\RestBundle\Tests\Integration\Subscription\Controller; -use PhpList\Core\Domain\Subscription\Repository\SubscriberAttributeValueRepository; use PhpList\RestBundle\Subscription\Controller\SubscriberAttributeValueController; use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; -use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\AttributeDefinitionFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberAttributeDefinitionFixture; +use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberAttributeValueFixture; use PhpList\RestBundle\Tests\Integration\Subscription\Fixtures\SubscriberFixture; class SubscriberAttributeValueControllerTest extends AbstractTestController { - protected function setUp(): void - { - parent::setUp(); - $this->attributeValueRepo = self::getContainer()->get(SubscriberAttributeValueRepository::class); - } public function testControllerIsAvailableViaContainer(): void { @@ -30,7 +25,7 @@ public function testCreateOrUpdateAttributeValue(): void { $this->loadFixtures([ SubscriberFixture::class, - AttributeDefinitionFixture::class, + SubscriberAttributeDefinitionFixture::class, ]); $subscriberId = 1; @@ -39,7 +34,7 @@ public function testCreateOrUpdateAttributeValue(): void $this->authenticatedJsonRequest( 'post', - "/api/v2/subscribers/attribute-values/$subscriberId/$definitionId", + '/api/v2/subscribers/attribute-values/' . $subscriberId . '/' . $definitionId, [], [], [], @@ -51,52 +46,13 @@ public function testCreateOrUpdateAttributeValue(): void self::assertSame('Test Country', $response['value']); } - public function testGetSubscriberAttributeValue(): void - { - $this->loadFixtures([ - SubscriberFixture::class, - AttributeDefinitionFixture::class, - ]); - - $this->authenticatedJsonRequest( - 'post', - '/api/v2/subscribers/attribute-values/1/1', - [], - [], - [], - json_encode(['value' => 'Test City']) - ); - - $this->assertHttpCreated(); - - $this->authenticatedJsonRequest( - 'get', - '/api/v2/subscribers/attribute-values/1/1' - ); - - $this->assertHttpOkay(); - $response = $this->getDecodedJsonResponseContent(); - self::assertSame('Test City', $response['value']); - } - public function testDeleteAttributeValue(): void { $this->loadFixtures([ SubscriberFixture::class, - AttributeDefinitionFixture::class, + SubscriberAttributeValueFixture::class, ]); - $this->authenticatedJsonRequest( - 'post', - '/api/v2/subscribers/attribute-values/1/1', - [], - [], - [], - json_encode(['value' => 'To Delete']) - ); - - $this->assertHttpCreated(); - $this->authenticatedJsonRequest( 'delete', '/api/v2/subscribers/attribute-values/1/1' @@ -109,7 +65,7 @@ public function testGetPaginatedAttributes(): void { $this->loadFixtures([ SubscriberFixture::class, - AttributeDefinitionFixture::class, + SubscriberAttributeDefinitionFixture::class, ]); $this->authenticatedJsonRequest( diff --git a/tests/Integration/Subscription/Controller/ListControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberListControllerTest.php similarity index 97% rename from tests/Integration/Subscription/Controller/ListControllerTest.php rename to tests/Integration/Subscription/Controller/SubscriberListControllerTest.php index 3e4a835..8eee311 100644 --- a/tests/Integration/Subscription/Controller/ListControllerTest.php +++ b/tests/Integration/Subscription/Controller/SubscriberListControllerTest.php @@ -5,7 +5,7 @@ namespace PhpList\RestBundle\Tests\Integration\Subscription\Controller; use PhpList\Core\Domain\Subscription\Repository\SubscriberListRepository; -use PhpList\RestBundle\Subscription\Controller\ListController; +use PhpList\RestBundle\Subscription\Controller\SubscriberListController; use PhpList\RestBundle\Tests\Integration\Common\AbstractTestController; use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorFixture; use PhpList\RestBundle\Tests\Integration\Identity\Fixtures\AdministratorTokenFixture; @@ -19,11 +19,14 @@ * @author Oliver Klee * @author Xheni Myrtaj */ -class ListControllerTest extends AbstractTestController +class SubscriberListControllerTest extends AbstractTestController { public function testControllerIsAvailableViaContainer() { - self::assertInstanceOf(ListController::class, self::getContainer()->get(ListController::class)); + self::assertInstanceOf( + SubscriberListController::class, + self::getContainer()->get(SubscriberListController::class) + ); } public function testGetListsWithoutSessionKeyReturnsForbiddenStatus() diff --git a/tests/Integration/Subscription/Fixtures/AttributeDefinitionFixture.php b/tests/Integration/Subscription/Fixtures/SubscriberAttributeDefinitionFixture.php similarity index 89% rename from tests/Integration/Subscription/Fixtures/AttributeDefinitionFixture.php rename to tests/Integration/Subscription/Fixtures/SubscriberAttributeDefinitionFixture.php index 8f971ec..c3386b4 100644 --- a/tests/Integration/Subscription/Fixtures/AttributeDefinitionFixture.php +++ b/tests/Integration/Subscription/Fixtures/SubscriberAttributeDefinitionFixture.php @@ -9,7 +9,7 @@ use Doctrine\Persistence\ObjectManager; use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition; -class AttributeDefinitionFixture extends Fixture implements FixtureInterface +class SubscriberAttributeDefinitionFixture extends Fixture implements FixtureInterface { public function load(ObjectManager $manager): void { diff --git a/tests/Integration/Subscription/Fixtures/SubscriberAttributeValueFixture.php b/tests/Integration/Subscription/Fixtures/SubscriberAttributeValueFixture.php new file mode 100644 index 0000000..c373e5d --- /dev/null +++ b/tests/Integration/Subscription/Fixtures/SubscriberAttributeValueFixture.php @@ -0,0 +1,36 @@ +setName('Country'); + $definition->setType('checkbox'); + $definition->setListOrder(1); + $definition->setDefaultValue('US'); + $definition->setRequired(true); + $definition->setTableName('list_attributes'); + + $manager->persist($definition); + + $subscriberRepository = $manager->getRepository(Subscriber::class); + $value = new SubscriberAttributeValue($definition, $subscriberRepository->find(1)); + $value->setValue('test value'); + + $manager->persist($value); + + $manager->flush(); + } +} diff --git a/tests/Unit/Identity/Request/CreateAdministratorRequestTest.php b/tests/Unit/Identity/Request/CreateAdministratorRequestTest.php new file mode 100644 index 0000000..6d43406 --- /dev/null +++ b/tests/Unit/Identity/Request/CreateAdministratorRequestTest.php @@ -0,0 +1,42 @@ +loginName = 'testuser'; + $request->password = 'password123'; + $request->email = 'test@example.com'; + $request->superUser = true; + + $dto = $request->getDto(); + + $this->assertEquals('testuser', $dto->loginName); + $this->assertEquals('password123', $dto->password); + $this->assertEquals('test@example.com', $dto->email); + $this->assertTrue($dto->isSuperUser); + } + + public function testGetDtoWithDefaultSuperUserValue(): void + { + $request = new CreateAdministratorRequest(); + $request->loginName = 'testuser'; + $request->password = 'password123'; + $request->email = 'test@example.com'; + + $dto = $request->getDto(); + + $this->assertEquals('testuser', $dto->loginName); + $this->assertEquals('password123', $dto->password); + $this->assertEquals('test@example.com', $dto->email); + $this->assertFalse($dto->isSuperUser); + } +} diff --git a/tests/Unit/Identity/Request/CreateSessionRequestTest.php b/tests/Unit/Identity/Request/CreateSessionRequestTest.php new file mode 100644 index 0000000..9f9580d --- /dev/null +++ b/tests/Unit/Identity/Request/CreateSessionRequestTest.php @@ -0,0 +1,24 @@ +loginName = 'testuser'; + $request->password = 'password123'; + + $dto = $request->getDto(); + + $this->assertSame($request, $dto); + $this->assertEquals('testuser', $dto->loginName); + $this->assertEquals('password123', $dto->password); + } +} diff --git a/tests/Unit/Identity/Request/UpdateAdministratorRequestTest.php b/tests/Unit/Identity/Request/UpdateAdministratorRequestTest.php new file mode 100644 index 0000000..5bdbc1d --- /dev/null +++ b/tests/Unit/Identity/Request/UpdateAdministratorRequestTest.php @@ -0,0 +1,44 @@ +administratorId = 123; + $request->loginName = 'testuser'; + $request->password = 'password123'; + $request->email = 'test@example.com'; + $request->superAdmin = true; + + $dto = $request->getDto(); + + $this->assertEquals(123, $dto->administratorId); + $this->assertEquals('testuser', $dto->loginName); + $this->assertEquals('password123', $dto->password); + $this->assertEquals('test@example.com', $dto->email); + $this->assertTrue($dto->superAdmin); + } + + public function testGetDtoWithNullValues(): void + { + $request = new UpdateAdministratorRequest(); + $request->administratorId = 456; + + $dto = $request->getDto(); + + $this->assertEquals(456, $dto->administratorId); + $this->assertNull($dto->loginName); + $this->assertNull($dto->password); + $this->assertNull($dto->email); + $this->assertNull($dto->superAdmin); + } +} diff --git a/tests/Unit/Messaging/Request/CreateMessageRequestTest.php b/tests/Unit/Messaging/Request/CreateMessageRequestTest.php new file mode 100644 index 0000000..be0ff32 --- /dev/null +++ b/tests/Unit/Messaging/Request/CreateMessageRequestTest.php @@ -0,0 +1,92 @@ +contentDto = $this->createMock(MessageContentDto::class); + $this->formatDto = $this->createMock(MessageFormatDto::class); + $this->metadataDto = $this->createMock(MessageMetadataDto::class); + $this->optionsDto = $this->createMock(MessageOptionsDto::class); + $this->scheduleDto = $this->createMock(MessageScheduleDto::class); + + $contentRequest = $this->createMock(MessageContentRequest::class); + $contentRequest->method('getDto')->willReturn($this->contentDto); + + $formatRequest = $this->createMock(MessageFormatRequest::class); + $formatRequest->method('getDto')->willReturn($this->formatDto); + + $metadataRequest = $this->createMock(MessageMetadataRequest::class); + $metadataRequest->method('getDto')->willReturn($this->metadataDto); + + $optionsRequest = $this->createMock(MessageOptionsRequest::class); + $optionsRequest->method('getDto')->willReturn($this->optionsDto); + + $scheduleRequest = $this->createMock(MessageScheduleRequest::class); + $scheduleRequest->method('getDto')->willReturn($this->scheduleDto); + + $this->request = new CreateMessageRequest(); + $this->request->content = $contentRequest; + $this->request->format = $formatRequest; + $this->request->metadata = $metadataRequest; + $this->request->options = $optionsRequest; + $this->request->schedule = $scheduleRequest; + } + + public function testGetDtoReturnsCorrectDto(): void + { + $this->request->templateId = 123; + + $dto = $this->request->getDto(); + + $this->assertInstanceOf(CreateMessageDto::class, $dto); + $this->assertSame($this->contentDto, $dto->content); + $this->assertSame($this->formatDto, $dto->format); + $this->assertSame($this->metadataDto, $dto->metadata); + $this->assertSame($this->optionsDto, $dto->options); + $this->assertSame($this->scheduleDto, $dto->schedule); + $this->assertEquals(123, $dto->templateId); + } + + public function testGetDtoWithNullTemplateId(): void + { + $this->request->templateId = null; + + $dto = $this->request->getDto(); + + $this->assertInstanceOf(CreateMessageDto::class, $dto); + $this->assertSame($this->contentDto, $dto->content); + $this->assertSame($this->formatDto, $dto->format); + $this->assertSame($this->metadataDto, $dto->metadata); + $this->assertSame($this->optionsDto, $dto->options); + $this->assertSame($this->scheduleDto, $dto->schedule); + $this->assertNull($dto->templateId); + } +} diff --git a/tests/Unit/Messaging/Request/CreateTemplateRequestTest.php b/tests/Unit/Messaging/Request/CreateTemplateRequestTest.php new file mode 100644 index 0000000..8b2b847 --- /dev/null +++ b/tests/Unit/Messaging/Request/CreateTemplateRequestTest.php @@ -0,0 +1,80 @@ +request = new CreateTemplateRequest(); + } + + public function testGetDtoReturnsCorrectDto(): void + { + $this->request->title = 'Test Template'; + $this->request->content = 'Test content with [CONTENT] placeholder'; + $this->request->text = 'Plain text with [TEXT] placeholder'; + $this->request->checkLinks = true; + $this->request->checkImages = true; + $this->request->checkExternalImages = true; + + $dto = $this->request->getDto(); + + $this->assertInstanceOf(CreateTemplateDto::class, $dto); + $this->assertEquals('Test Template', $dto->title); + $this->assertEquals('Test content with [CONTENT] placeholder', $dto->content); + $this->assertEquals('Plain text with [TEXT] placeholder', $dto->text); + $this->assertNull($dto->fileContent); + $this->assertTrue($dto->shouldCheckLinks); + $this->assertTrue($dto->shouldCheckImages); + $this->assertTrue($dto->shouldCheckExternalImages); + } + + public function testGetDtoWithDefaultValues(): void + { + $this->request->title = 'Test Template'; + $this->request->content = 'Test content with [CONTENT] placeholder'; + + $dto = $this->request->getDto(); + + $this->assertInstanceOf(CreateTemplateDto::class, $dto); + $this->assertEquals('Test Template', $dto->title); + $this->assertEquals('Test content with [CONTENT] placeholder', $dto->content); + $this->assertNull($dto->text); + $this->assertNull($dto->fileContent); + $this->assertFalse($dto->shouldCheckLinks); + $this->assertFalse($dto->shouldCheckImages); + $this->assertFalse($dto->shouldCheckExternalImages); + } + + public function testGetDtoWithUploadedFile(): void + { + $tempFile = tempnam(sys_get_temp_dir(), 'test'); + file_put_contents($tempFile, 'Test file content'); + + $uploadedFile = $this->createMock(UploadedFile::class); + $uploadedFile->method('getPathname')->willReturn($tempFile); + + $this->request->title = 'Test Template'; + $this->request->content = 'Test content with [CONTENT] placeholder'; + $this->request->file = $uploadedFile; + + $dto = $this->request->getDto(); + + $this->assertInstanceOf(CreateTemplateDto::class, $dto); + $this->assertEquals('Test Template', $dto->title); + $this->assertEquals('Test content with [CONTENT] placeholder', $dto->content); + $this->assertEquals('Test file content', $dto->fileContent); + + unlink($tempFile); + } +} diff --git a/tests/Unit/Messaging/Request/UpdateMessageRequestTest.php b/tests/Unit/Messaging/Request/UpdateMessageRequestTest.php new file mode 100644 index 0000000..1b24254 --- /dev/null +++ b/tests/Unit/Messaging/Request/UpdateMessageRequestTest.php @@ -0,0 +1,95 @@ +contentDto = $this->createMock(MessageContentDto::class); + $this->formatDto = $this->createMock(MessageFormatDto::class); + $this->metadataDto = $this->createMock(MessageMetadataDto::class); + $this->optionsDto = $this->createMock(MessageOptionsDto::class); + $this->scheduleDto = $this->createMock(MessageScheduleDto::class); + + $contentRequest = $this->createMock(MessageContentRequest::class); + $contentRequest->method('getDto')->willReturn($this->contentDto); + + $formatRequest = $this->createMock(MessageFormatRequest::class); + $formatRequest->method('getDto')->willReturn($this->formatDto); + + $metadataRequest = $this->createMock(MessageMetadataRequest::class); + $metadataRequest->method('getDto')->willReturn($this->metadataDto); + + $optionsRequest = $this->createMock(MessageOptionsRequest::class); + $optionsRequest->method('getDto')->willReturn($this->optionsDto); + + $scheduleRequest = $this->createMock(MessageScheduleRequest::class); + $scheduleRequest->method('getDto')->willReturn($this->scheduleDto); + + $this->request = new UpdateMessageRequest(); + $this->request->messageId = 123; + $this->request->content = $contentRequest; + $this->request->format = $formatRequest; + $this->request->metadata = $metadataRequest; + $this->request->options = $optionsRequest; + $this->request->schedule = $scheduleRequest; + } + + public function testGetDtoReturnsCorrectDto(): void + { + $this->request->templateId = 456; + + $dto = $this->request->getDto(); + + $this->assertInstanceOf(UpdateMessageDto::class, $dto); + $this->assertEquals(123, $dto->messageId); + $this->assertSame($this->contentDto, $dto->content); + $this->assertSame($this->formatDto, $dto->format); + $this->assertSame($this->metadataDto, $dto->metadata); + $this->assertSame($this->optionsDto, $dto->options); + $this->assertSame($this->scheduleDto, $dto->schedule); + $this->assertEquals(456, $dto->templateId); + } + + public function testGetDtoWithNullTemplateId(): void + { + $this->request->templateId = null; + + $dto = $this->request->getDto(); + + $this->assertInstanceOf(UpdateMessageDto::class, $dto); + $this->assertEquals(123, $dto->messageId); + $this->assertSame($this->contentDto, $dto->content); + $this->assertSame($this->formatDto, $dto->format); + $this->assertSame($this->metadataDto, $dto->metadata); + $this->assertSame($this->optionsDto, $dto->options); + $this->assertSame($this->scheduleDto, $dto->schedule); + $this->assertNull($dto->templateId); + } +} diff --git a/tests/Unit/Subscription/Request/CreateAttributeDefinitionRequestTest.php b/tests/Unit/Subscription/Request/CreateAttributeDefinitionRequestTest.php new file mode 100644 index 0000000..d55376b --- /dev/null +++ b/tests/Unit/Subscription/Request/CreateAttributeDefinitionRequestTest.php @@ -0,0 +1,49 @@ +name = 'Test Attribute'; + $request->type = 'text'; + $request->order = 5; + $request->defaultValue = 'default'; + $request->required = true; + $request->tableName = 'test_table'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(AttributeDefinitionDto::class, $dto); + $this->assertEquals('Test Attribute', $dto->name); + $this->assertEquals('text', $dto->type); + $this->assertEquals(5, $dto->listOrder); + $this->assertEquals('default', $dto->defaultValue); + $this->assertTrue($dto->required); + $this->assertEquals('test_table', $dto->tableName); + } + + public function testGetDtoWithDefaultValues(): void + { + $request = new CreateAttributeDefinitionRequest(); + $request->name = 'Test Attribute'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(AttributeDefinitionDto::class, $dto); + $this->assertEquals('Test Attribute', $dto->name); + $this->assertNull($dto->type); + $this->assertNull($dto->listOrder); + $this->assertNull($dto->defaultValue); + $this->assertFalse($dto->required); + $this->assertNull($dto->tableName); + } +} diff --git a/tests/Unit/Subscription/Request/CreateSubscriberListRequestTest.php b/tests/Unit/Subscription/Request/CreateSubscriberListRequestTest.php new file mode 100644 index 0000000..5f6f529 --- /dev/null +++ b/tests/Unit/Subscription/Request/CreateSubscriberListRequestTest.php @@ -0,0 +1,43 @@ +name = 'Test List'; + $request->public = true; + $request->listPosition = 5; + $request->description = 'Test description'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(CreateSubscriberListDto::class, $dto); + $this->assertEquals('Test List', $dto->name); + $this->assertTrue($dto->isPublic); + $this->assertEquals(5, $dto->listPosition); + $this->assertEquals('Test description', $dto->description); + } + + public function testGetDtoWithDefaultValues(): void + { + $request = new CreateSubscriberListRequest(); + $request->name = 'Test List'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(CreateSubscriberListDto::class, $dto); + $this->assertEquals('Test List', $dto->name); + $this->assertFalse($dto->isPublic); + $this->assertNull($dto->listPosition); + $this->assertNull($dto->description); + } +} diff --git a/tests/Unit/Subscription/Request/CreateSubscriberRequestTest.php b/tests/Unit/Subscription/Request/CreateSubscriberRequestTest.php new file mode 100644 index 0000000..3e81e47 --- /dev/null +++ b/tests/Unit/Subscription/Request/CreateSubscriberRequestTest.php @@ -0,0 +1,40 @@ +email = 'subscriber@example.com'; + $request->requestConfirmation = true; + $request->htmlEmail = false; + + $dto = $request->getDto(); + + $this->assertInstanceOf(CreateSubscriberDto::class, $dto); + $this->assertEquals('subscriber@example.com', $dto->email); + $this->assertTrue($dto->requestConfirmation); + $this->assertFalse($dto->htmlEmail); + } + + public function testGetDtoWithNullValues(): void + { + $request = new CreateSubscriberRequest(); + $request->email = 'subscriber@example.com'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(CreateSubscriberDto::class, $dto); + $this->assertEquals('subscriber@example.com', $dto->email); + $this->assertNull($dto->requestConfirmation); + $this->assertNull($dto->htmlEmail); + } +} diff --git a/tests/Unit/Subscription/Request/SubscriptionRequestTest.php b/tests/Unit/Subscription/Request/SubscriptionRequestTest.php new file mode 100644 index 0000000..eb0171c --- /dev/null +++ b/tests/Unit/Subscription/Request/SubscriptionRequestTest.php @@ -0,0 +1,32 @@ +emails = ['test1@example.com', 'test2@example.com']; + + $dto = $request->getDto(); + + $this->assertSame($request, $dto); + $this->assertEquals(['test1@example.com', 'test2@example.com'], $dto->emails); + } + + public function testGetDtoWithEmptyEmails(): void + { + $request = new SubscriptionRequest(); + + $dto = $request->getDto(); + + $this->assertSame($request, $dto); + $this->assertEquals([], $dto->emails); + } +} diff --git a/tests/Unit/Subscription/Request/UpdateAttributeDefinitionRequestTest.php b/tests/Unit/Subscription/Request/UpdateAttributeDefinitionRequestTest.php new file mode 100644 index 0000000..512b534 --- /dev/null +++ b/tests/Unit/Subscription/Request/UpdateAttributeDefinitionRequestTest.php @@ -0,0 +1,49 @@ +name = 'Test Attribute'; + $request->type = 'text'; + $request->order = 5; + $request->defaultValue = 'default'; + $request->required = true; + $request->tableName = 'test_table'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(AttributeDefinitionDto::class, $dto); + $this->assertEquals('Test Attribute', $dto->name); + $this->assertEquals('text', $dto->type); + $this->assertEquals(5, $dto->listOrder); + $this->assertEquals('default', $dto->defaultValue); + $this->assertTrue($dto->required); + $this->assertEquals('test_table', $dto->tableName); + } + + public function testGetDtoWithDefaultValues(): void + { + $request = new UpdateAttributeDefinitionRequest(); + $request->name = 'Test Attribute'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(AttributeDefinitionDto::class, $dto); + $this->assertEquals('Test Attribute', $dto->name); + $this->assertNull($dto->type); + $this->assertNull($dto->listOrder); + $this->assertNull($dto->defaultValue); + $this->assertFalse($dto->required); + $this->assertNull($dto->tableName); + } +} diff --git a/tests/Unit/Subscription/Request/UpdateSubscriberRequestTest.php b/tests/Unit/Subscription/Request/UpdateSubscriberRequestTest.php new file mode 100644 index 0000000..16e5257 --- /dev/null +++ b/tests/Unit/Subscription/Request/UpdateSubscriberRequestTest.php @@ -0,0 +1,35 @@ +subscriberId = 123; + $request->email = 'subscriber@example.com'; + $request->confirmed = true; + $request->blacklisted = false; + $request->htmlEmail = true; + $request->disabled = false; + $request->additionalData = 'Some additional data'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(UpdateSubscriberDto::class, $dto); + $this->assertEquals(123, $dto->subscriberId); + $this->assertEquals('subscriber@example.com', $dto->email); + $this->assertTrue($dto->confirmed); + $this->assertFalse($dto->blacklisted); + $this->assertTrue($dto->htmlEmail); + $this->assertFalse($dto->disabled); + $this->assertEquals('Some additional data', $dto->additionalData); + } +} From 62b2aeaf87be155589e29c971878569a2b23a427 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sat, 17 May 2025 19:44:42 +0400 Subject: [PATCH 10/21] ISSUE-345: update normalizers --- config/services/normalizers.yml | 4 ++ .../OpenApi/SwaggerSchemasResponse.php | 25 ++++++++++- .../Serializer/SubscriberNormalizer.php | 13 +++--- .../Serializer/SubscriberOnlyNormalizer.php | 41 +++++++++++++++++++ .../Serializer/SubscriptionNormalizer.php | 4 +- .../SubscriberListControllerTest.php | 18 +++++--- .../Serializer/SubscriberNormalizerTest.php | 13 +++--- .../Serializer/SubscriptionNormalizerTest.php | 8 ++-- 8 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 src/Subscription/Serializer/SubscriberOnlyNormalizer.php diff --git a/config/services/normalizers.yml b/config/services/normalizers.yml index f2c3e43..43878f9 100644 --- a/config/services/normalizers.yml +++ b/config/services/normalizers.yml @@ -15,6 +15,10 @@ services: tags: [ 'serializer.normalizer' ] autowire: true + PhpList\RestBundle\Subscription\Serializer\SubscriberOnlyNormalizer: + tags: [ 'serializer.normalizer' ] + autowire: true + PhpList\RestBundle\Identity\Serializer\AdministratorTokenNormalizer: tags: [ 'serializer.normalizer' ] autowire: true diff --git a/src/Subscription/OpenApi/SwaggerSchemasResponse.php b/src/Subscription/OpenApi/SwaggerSchemasResponse.php index 638a70d..edc5ea1 100644 --- a/src/Subscription/OpenApi/SwaggerSchemasResponse.php +++ b/src/Subscription/OpenApi/SwaggerSchemasResponse.php @@ -18,7 +18,10 @@ format: 'date-time', example: '2022-12-01T10:00:00Z' ), + new OA\Property(property: 'list_position', type: 'integer', example: 1), + new OA\Property(property: 'subject_prefix', type: 'string', example: 'Newsletter: '), new OA\Property(property: 'public', type: 'boolean', example: true), + new OA\Property(property: 'category', type: 'string', example: 'News'), ], type: 'object' )] @@ -47,10 +50,30 @@ ], type: 'object' )] +#[OA\Schema( + schema: 'SubscriberOnly', + properties: [ + new OA\Property(property: 'id', type: 'integer', example: 1), + new OA\Property(property: 'email', type: 'string', example: 'subscriber@example.com'), + new OA\Property( + property: 'created_at', + type: 'string', + format: 'date-time', + example: '2023-01-01T12:00:00Z', + ), + new OA\Property(property: 'confirmed', type: 'boolean', example: true), + new OA\Property(property: 'blacklisted', type: 'boolean', example: false), + new OA\Property(property: 'bounce_count', type: 'integer', example: 0), + new OA\Property(property: 'unique_id', type: 'string', example: '69f4e92cf50eafca9627f35704f030f4'), + new OA\Property(property: 'html_email', type: 'boolean', example: true), + new OA\Property(property: 'disabled', type: 'boolean', example: false), + ], + type: 'object' +)] #[OA\Schema( schema: 'Subscription', properties: [ - new OA\Property(property: 'subscriber', ref: '#/components/schemas/Subscriber'), + new OA\Property(property: 'subscriber', ref: '#/components/schemas/SubscriberOnly'), new OA\Property(property: 'subscriber_list', ref: '#/components/schemas/SubscriberList'), new OA\Property( property: 'subscription_date', diff --git a/src/Subscription/Serializer/SubscriberNormalizer.php b/src/Subscription/Serializer/SubscriberNormalizer.php index 404ee18..84353ff 100644 --- a/src/Subscription/Serializer/SubscriberNormalizer.php +++ b/src/Subscription/Serializer/SubscriberNormalizer.php @@ -10,6 +10,10 @@ class SubscriberNormalizer implements NormalizerInterface { + public function __construct(private readonly SubscriberListNormalizer $subscriberListNormalizer) + { + } + /** * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -30,14 +34,7 @@ public function normalize($object, string $format = null, array $context = []): 'html_email' => $object->hasHtmlEmail(), 'disabled' => $object->isDisabled(), 'subscribed_lists' => array_map(function (Subscription $subscription) { - return [ - 'id' => $subscription->getSubscriberList()->getId(), - 'name' => $subscription->getSubscriberList()->getName(), - 'description' => $subscription->getSubscriberList()->getDescription(), - 'created_at' => $subscription->getSubscriberList()->getCreatedAt()->format('Y-m-d\TH:i:sP'), - 'public' => $subscription->getSubscriberList()->isPublic(), - 'subscription_date' => $subscription->getCreatedAt()->format('Y-m-d\TH:i:sP'), - ]; + return $this->subscriberListNormalizer->normalize($subscription->getSubscriberList()); }, $object->getSubscriptions()->toArray()), ]; } diff --git a/src/Subscription/Serializer/SubscriberOnlyNormalizer.php b/src/Subscription/Serializer/SubscriberOnlyNormalizer.php new file mode 100644 index 0000000..e4cc95a --- /dev/null +++ b/src/Subscription/Serializer/SubscriberOnlyNormalizer.php @@ -0,0 +1,41 @@ + $object->getId(), + 'email' => $object->getEmail(), + 'created_at' => $object->getCreatedAt()->format('Y-m-d\TH:i:sP'), + 'confirmed' => $object->isConfirmed(), + 'blacklisted' => $object->isBlacklisted(), + 'bounce_count' => $object->getBounceCount(), + 'unique_id' => $object->getUniqueId(), + 'html_email' => $object->hasHtmlEmail(), + 'disabled' => $object->isDisabled(), + ]; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof Subscriber; + } +} diff --git a/src/Subscription/Serializer/SubscriptionNormalizer.php b/src/Subscription/Serializer/SubscriptionNormalizer.php index de476fb..7f68a7e 100644 --- a/src/Subscription/Serializer/SubscriptionNormalizer.php +++ b/src/Subscription/Serializer/SubscriptionNormalizer.php @@ -9,11 +9,11 @@ class SubscriptionNormalizer implements NormalizerInterface { - private SubscriberNormalizer $subscriberNormalizer; + private SubscriberOnlyNormalizer $subscriberNormalizer; private SubscriberListNormalizer $subscriberListNormalizer; public function __construct( - SubscriberNormalizer $subscriberNormalizer, + SubscriberOnlyNormalizer $subscriberNormalizer, SubscriberListNormalizer $subscriberListNormalizer ) { $this->subscriberNormalizer = $subscriberNormalizer; diff --git a/tests/Integration/Subscription/Controller/SubscriberListControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberListControllerTest.php index 8eee311..eba6821 100644 --- a/tests/Integration/Subscription/Controller/SubscriberListControllerTest.php +++ b/tests/Integration/Subscription/Controller/SubscriberListControllerTest.php @@ -270,10 +270,12 @@ public function testGetListMembersWithCurrentSessionKeyForExistingListWithSubscr [ 'id' => 2, 'name' => 'More news', - 'description' => '', 'created_at' => '2016-06-22T15:01:17+00:00', + 'description' => '', + 'list_position' => 12, + 'subject_prefix' => '', 'public' => true, - 'subscription_date' => '2016-07-22T15:01:17+00:00', + 'category' => '', ], ], ], [ @@ -290,18 +292,22 @@ public function testGetListMembersWithCurrentSessionKeyForExistingListWithSubscr [ 'id' => 2, 'name' => 'More news', - 'description' => '', 'created_at' => '2016-06-22T15:01:17+00:00', + 'description' => '', + 'list_position' => 12, + 'subject_prefix' => '', 'public' => true, - 'subscription_date' => '2016-08-22T15:01:17+00:00', + 'category' => '', ], [ 'id' => 1, 'name' => 'News', - 'description' => 'News (and some fun stuff)', 'created_at' => '2016-06-22T15:01:17+00:00', + 'description' => 'News (and some fun stuff)', + 'list_position' => 12, + 'subject_prefix' => 'phpList', 'public' => true, - 'subscription_date' => '2016-09-22T15:01:17+00:00', + 'category' => 'news', ], ], ], diff --git a/tests/Unit/Subscription/Serializer/SubscriberNormalizerTest.php b/tests/Unit/Subscription/Serializer/SubscriberNormalizerTest.php index 8a87c7c..5e8da09 100644 --- a/tests/Unit/Subscription/Serializer/SubscriberNormalizerTest.php +++ b/tests/Unit/Subscription/Serializer/SubscriberNormalizerTest.php @@ -9,6 +9,7 @@ use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\Core\Domain\Subscription\Model\Subscription; +use PhpList\RestBundle\Subscription\Serializer\SubscriberListNormalizer; use PhpList\RestBundle\Subscription\Serializer\SubscriberNormalizer; use PHPUnit\Framework\TestCase; use stdClass; @@ -17,7 +18,7 @@ class SubscriberNormalizerTest extends TestCase { public function testSupportsNormalization(): void { - $normalizer = new SubscriberNormalizer(); + $normalizer = new SubscriberNormalizer(new SubscriberListNormalizer()); $subscriber = $this->createMock(Subscriber::class); $this->assertTrue($normalizer->supportsNormalization($subscriber)); @@ -49,7 +50,7 @@ public function testNormalize(): void $subscriber->method('isDisabled')->willReturn(false); $subscriber->method('getSubscriptions')->willReturn(new ArrayCollection([$subscription])); - $normalizer = new SubscriberNormalizer(); + $normalizer = new SubscriberNormalizer(new SubscriberListNormalizer()); $expected = [ 'id' => 101, @@ -65,10 +66,12 @@ public function testNormalize(): void [ 'id' => 1, 'name' => 'News', - 'description' => 'Latest news', 'created_at' => '2025-01-01T00:00:00+00:00', + 'description' => 'Latest news', + 'list_position' => null, + 'subject_prefix' => null, 'public' => true, - 'subscription_date' => '2025-01-10T00:00:00+00:00' + 'category' => '', ] ] ]; @@ -78,7 +81,7 @@ public function testNormalize(): void public function testNormalizeWithInvalidObject(): void { - $normalizer = new SubscriberNormalizer(); + $normalizer = new SubscriberNormalizer(new SubscriberListNormalizer()); $this->assertSame([], $normalizer->normalize(new stdClass())); } } diff --git a/tests/Unit/Subscription/Serializer/SubscriptionNormalizerTest.php b/tests/Unit/Subscription/Serializer/SubscriptionNormalizerTest.php index 4e3b409..4e99e5e 100644 --- a/tests/Unit/Subscription/Serializer/SubscriptionNormalizerTest.php +++ b/tests/Unit/Subscription/Serializer/SubscriptionNormalizerTest.php @@ -9,7 +9,7 @@ use PhpList\Core\Domain\Subscription\Model\SubscriberList; use PhpList\Core\Domain\Subscription\Model\Subscription; use PhpList\RestBundle\Subscription\Serializer\SubscriberListNormalizer; -use PhpList\RestBundle\Subscription\Serializer\SubscriberNormalizer; +use PhpList\RestBundle\Subscription\Serializer\SubscriberOnlyNormalizer; use PhpList\RestBundle\Subscription\Serializer\SubscriptionNormalizer; use PHPUnit\Framework\TestCase; @@ -18,7 +18,7 @@ class SubscriptionNormalizerTest extends TestCase public function testSupportsNormalization(): void { $normalizer = new SubscriptionNormalizer( - $this->createMock(SubscriberNormalizer::class), + $this->createMock(SubscriberOnlyNormalizer::class), $this->createMock(SubscriberListNormalizer::class) ); @@ -38,7 +38,7 @@ public function testNormalize(): void $subscription->method('getSubscriberList')->willReturn($subscriberList); $subscription->method('getCreatedAt')->willReturn($subscriptionDate); - $subscriberNormalizer = $this->createMock(SubscriberNormalizer::class); + $subscriberNormalizer = $this->createMock(SubscriberOnlyNormalizer::class); $subscriberListNormalizer = $this->createMock(SubscriberListNormalizer::class); $subscriberNormalizer->method('normalize')->with($subscriber)->willReturn(['subscriber_data']); @@ -58,7 +58,7 @@ public function testNormalize(): void public function testNormalizeWithInvalidObjectReturnsEmptyArray(): void { $normalizer = new SubscriptionNormalizer( - $this->createMock(SubscriberNormalizer::class), + $this->createMock(SubscriberOnlyNormalizer::class), $this->createMock(SubscriberListNormalizer::class) ); From 5239f630faadd1bc3bc5ec49d1e82baed7f6185b Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sat, 17 May 2025 21:50:09 +0400 Subject: [PATCH 11/21] ISSUE-345: admin attributes --- config/services/normalizers.yml | 8 + .../AdminAttributeDefinitionController.php | 366 ++++++++++++++++++ .../AdminAttributeValueController.php | 345 +++++++++++++++++ .../CreateAttributeDefinitionRequest.php | 33 ++ .../UpdateAttributeDefinitionRequest.php | 33 ++ .../AdminAttributeDefinitionNormalizer.php | 39 ++ .../AdminAttributeValueNormalizer.php | 41 ++ .../SubscriberAttributeValueController.php | 4 +- 8 files changed, 867 insertions(+), 2 deletions(-) create mode 100644 src/Identity/Controller/AdminAttributeDefinitionController.php create mode 100644 src/Identity/Controller/AdminAttributeValueController.php create mode 100644 src/Identity/Request/CreateAttributeDefinitionRequest.php create mode 100644 src/Identity/Request/UpdateAttributeDefinitionRequest.php create mode 100644 src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php create mode 100644 src/Identity/Serializer/AdminAttributeValueNormalizer.php diff --git a/config/services/normalizers.yml b/config/services/normalizers.yml index 43878f9..430a8ec 100644 --- a/config/services/normalizers.yml +++ b/config/services/normalizers.yml @@ -47,6 +47,14 @@ services: tags: [ 'serializer.normalizer' ] autowire: true + PhpList\RestBundle\Identity\Serializer\AdminAttributeDefinitionNormalizer: + tags: [ 'serializer.normalizer' ] + autowire: true + + PhpList\RestBundle\Identity\Serializer\AdminAttributeValueNormalizer: + tags: [ 'serializer.normalizer' ] + autowire: true + PhpList\RestBundle\Subscription\Serializer\AttributeDefinitionNormalizer: tags: [ 'serializer.normalizer' ] autowire: true diff --git a/src/Identity/Controller/AdminAttributeDefinitionController.php b/src/Identity/Controller/AdminAttributeDefinitionController.php new file mode 100644 index 0000000..01c3997 --- /dev/null +++ b/src/Identity/Controller/AdminAttributeDefinitionController.php @@ -0,0 +1,366 @@ +definitionManager = $definitionManager; + $this->normalizer = $normalizer; + $this->paginatedDataProvider = $paginatedDataProvider; + } + + #[Route('', name: 'create_admin_attribute_definition', methods: ['POST'])] + #[OA\Post( + path: '/administrators/attributes', + description: 'Returns created admin attribute definition.', + summary: 'Create an admin attribute definition.', + requestBody: new OA\RequestBody( + description: 'Pass parameters to create admin attribute.', + required: true, + content: new OA\JsonContent( + 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: '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'), + ] + ) + ), + tags: ['admin-attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema( + type: 'string' + ) + ) + ], + responses: [ + new OA\Response( + response: 201, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/AttributeDefinition') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 422, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse') + ), + ] + )] + public function create(Request $request): JsonResponse + { + $this->requireAuthentication($request); + + /** @var CreateAttributeDefinitionRequest $definitionRequest */ + $definitionRequest = $this->validator->validate($request, CreateAttributeDefinitionRequest::class); + + $attributeDefinition = $this->definitionManager->create($definitionRequest->getDto()); + $json = $this->normalizer->normalize($attributeDefinition, 'json'); + + return $this->json($json, Response::HTTP_CREATED); + } + + #[Route('/{definitionId}', name: 'update_admin_attribute_definition', methods: ['PUT'])] + #[OA\Put( + path: '/administrators/attributes/{definitionId}', + description: 'Returns updated admin attribute definition.', + summary: 'Update an admin attribute definition.', + requestBody: new OA\RequestBody( + description: 'Pass parameters to update admin attribute.', + required: true, + content: new OA\JsonContent( + 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: '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'), + ] + ) + ), + tags: ['admin-attributes'], + parameters: [ + new OA\Parameter( + name: 'definitionId', + description: 'Definition ID', + in: 'path', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/AttributeDefinition') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 422, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse') + ), + ] + )] + public function update( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?AdminAttributeDefinition $attributeDefinition, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$attributeDefinition) { + throw $this->createNotFoundException('Attribute definition not found.'); + } + + /** @var UpdateAttributeDefinitionRequest $definitionRequest */ + $definitionRequest = $this->validator->validate($request, UpdateAttributeDefinitionRequest::class); + + $attributeDefinition = $this->definitionManager->update( + attributeDefinition: $attributeDefinition, + attributeDefinitionDto: $definitionRequest->getDto(), + ); + $json = $this->normalizer->normalize($attributeDefinition, 'json'); + + return $this->json($json, Response::HTTP_OK); + } + + #[Route('/{definitionId}', name: 'delete_admin_attribute_definition', methods: ['DELETE'])] + #[OA\Delete( + path: '/administrators/attributes/{definitionId}', + description: 'Deletes a single admin attribute definition.', + summary: 'Deletes an attribute definition.', + tags: ['admin-attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'definitionId', + description: 'Definition ID', + in: 'path', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success' + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 404, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse') + ) + ] + )] + public function delete( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?AdminAttributeDefinition $attributeDefinition, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$attributeDefinition) { + throw $this->createNotFoundException('Attribute definition not found.'); + } + + $this->definitionManager->delete($attributeDefinition); + + return $this->json(null, Response::HTTP_NO_CONTENT); + } + + #[Route('', name: 'get_admin_attribute_definitions', methods: ['GET'])] + #[OA\Get( + path: '/administrators/attributes', + description: 'Returns a JSON list of all admin attribute definitions.', + summary: 'Gets a list of all DMIN attribute definitions.', + tags: ['admin-attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema( + type: 'string' + ) + ), + new OA\Parameter( + name: 'after_id', + description: 'Last id (starting from 0)', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 1, minimum: 1) + ), + new OA\Parameter( + name: 'limit', + description: 'Number of results per page', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 25, maximum: 100, minimum: 1) + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'items', + type: 'array', + items: new OA\Items(ref: '#/components/schemas/AttributeDefinition') + ), + new OA\Property(property: 'pagination', ref: '#/components/schemas/CursorPagination') + ], + type: 'object' + ) + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ) + ] + )] + public function getPaginated(Request $request): JsonResponse + { + $this->requireAuthentication($request); + + return $this->json( + $this->paginatedDataProvider->getPaginatedList( + $request, + $this->normalizer, + AdminAttributeDefinition::class, + ), + Response::HTTP_OK + ); + } + + #[Route('/{definitionId}', name: 'get_admin_attribute_definition', methods: ['GET'])] + #[OA\Get( + path: '/administrators/attributes/{definitionId}', + description: 'Returns a single attribute with specified ID.', + summary: 'Gets attribute with specified ID.', + tags: ['admin-attributes'], + parameters: [ + new OA\Parameter( + name: 'definitionId', + description: 'Definition ID', + in: 'path', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/AttributeDefinition') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 404, + description: 'Failure', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'message', + type: 'string', + example: 'There is no attribute with that ID.' + ) + ], + type: 'object' + ) + ) + ] + )] + public function getAttributeDefinition( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?AdminAttributeDefinition $attributeDefinition, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$attributeDefinition) { + throw $this->createNotFoundException('Attribute definition not found.'); + } + + return $this->json( + $this->normalizer->normalize($attributeDefinition), + Response::HTTP_OK + ); + } +} diff --git a/src/Identity/Controller/AdminAttributeValueController.php b/src/Identity/Controller/AdminAttributeValueController.php new file mode 100644 index 0000000..8f2afec --- /dev/null +++ b/src/Identity/Controller/AdminAttributeValueController.php @@ -0,0 +1,345 @@ +attributeManager = $attributeManager; + $this->normalizer = $normalizer; + $this->paginatedDataProvider = $paginatedDataProvider; + } + + #[Route('/{adminId}/{definitionId}', name: 'create_admin_attribute_value', methods: ['POST', 'PUT'])] + #[OA\Post( + path: '/administrators/attribute-values/{adminId}/{definitionId}', + description: 'Returns created/updated admin attribute.', + summary: 'Create/update an admin attribute.', + requestBody: new OA\RequestBody( + description: 'Pass parameters to create admin attribute.', + required: true, + content: new OA\JsonContent( + properties: [ + new OA\Property(property: 'value', type: 'string', example: 'United States'), + ] + ) + ), + tags: ['admin-attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'definitionId', + description: 'attribute definition id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'adminId', + description: 'Administrator id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + ], + responses: [ + new OA\Response( + response: 201, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/AdminAttributeValue') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 422, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse') + ), + ] + )] + public function createOrUpdate( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?AdminAttributeDefinition $definition = null, + #[MapEntity(mapping: ['adminId' => 'id'])] ?Administrator $admin = null, + ): JsonResponse { + $this->requireAuthentication($request); + + if (!$definition) { + throw $this->createNotFoundException('Attribute definition not found.'); + } + if (!$admin) { + throw $this->createNotFoundException('Administrator not found.'); + } + + $attributeDefinition = $this->attributeManager->createOrUpdate( + admin:$admin, + definition: $definition, + value: $request->toArray()['value'] ?? null + ); + $json = $this->normalizer->normalize($attributeDefinition, 'json'); + + return $this->json($json, Response::HTTP_CREATED); + } + + #[Route('/{adminId}/{definitionId}', name: 'delete_admin_attribute', methods: ['DELETE'])] + #[OA\Delete( + path: '/administrators/attribute-values/{admin}/{definitionId}', + description: 'Deletes a single admin attribute.', + summary: 'Deletes an attribute.', + tags: ['admin-attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'definitionId', + description: 'attribute definition id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'adminId', + description: 'Administrator id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success' + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 404, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse') + ) + ] + )] + public function delete( + Request $request, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?AdminAttributeDefinition $definition = null, + #[MapEntity(mapping: ['adminId' => 'id'])] ?Administrator $admin = null, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$definition || !$admin) { + throw $this->createNotFoundException('Administrator attribute not found.'); + } + $attribute = $this->attributeManager->getAdminAttribute($admin->getId(), $definition->getId()); + if ($attribute === null) { + throw $this->createNotFoundException('Administrator attribute not found.'); + } + $this->attributeManager->delete($attribute); + + return $this->json(null, Response::HTTP_NO_CONTENT); + } + + #[Route('/{adminId}', name: 'get_admin_attribute_list', methods: ['GET'])] + #[OA\Get( + path: '/administrators/attribute-values/{adminId}', + description: 'Returns a JSON list of all admin attributes.', + summary: 'Gets a list of all admin attributes.', + tags: ['admin-attributes'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'adminId', + description: 'Administrator id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'after_id', + description: 'Last id (starting from 0)', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 1, minimum: 1) + ), + new OA\Parameter( + name: 'limit', + description: 'Number of results per page', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 25, maximum: 100, minimum: 1) + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'items', + type: 'array', + items: new OA\Items(ref: '#/components/schemas/AdminAttributeValue') + ), + new OA\Property(property: 'pagination', ref: '#/components/schemas/CursorPagination') + ], + type: 'object' + ) + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ) + ] + )] + public function getPaginated( + Request $request, + #[MapEntity(mapping: ['adminId' => 'id'])] ?Administrator $admin = null, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$admin) { + $this->createNotFoundException('Administrator not found.'); + } + + $filter = (new AdminAttributeValueFilter())->setAdminId($admin->getId()); + + return $this->json( + $this->paginatedDataProvider->getPaginatedList( + $request, + $this->normalizer, + AdminAttributeValue::class, + $filter + ), + Response::HTTP_OK + ); + } + + #[Route('/{adminId}/{definitionId}', name: 'get_admin_attribute', methods: ['GET'])] + #[OA\Get( + path: '/administrators/attribute-values/{adminId}/{definitionId}', + description: 'Returns a single attribute.', + summary: 'Gets admin attribute.', + tags: ['admin-attributes'], + parameters: [ + new OA\Parameter( + name: 'definitionId', + description: 'attribute definition id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'adminId', + description: 'Administrator id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent(ref: '#/components/schemas/AdminAttributeValue') + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ), + new OA\Response( + response: 404, + description: 'Failure', + content: new OA\JsonContent( + properties: [ + new OA\Property( + property: 'message', + type: 'string', + example: 'There is no attribute with that ID.' + ) + ], + type: 'object' + ) + ) + ] + )] + public function getAttributeDefinition( + Request $request, + #[MapEntity(mapping: ['adminId' => 'id'])] ?AdminAttributeDefinition $admin, + #[MapEntity(mapping: ['definitionId' => 'id'])] ?AdminAttributeDefinition $definition, + ): JsonResponse { + $this->requireAuthentication($request); + if (!$definition || !$admin) { + throw $this->createNotFoundException('Administrator attribute not found.'); + } + $attribute = $this->attributeManager->getAdminAttribute( + adminId: $admin->getId(), + attributeDefinitionId: $definition->getId() + ); + $this->attributeManager->delete($attribute); + + return $this->json( + $this->normalizer->normalize($attribute), + Response::HTTP_OK + ); + } +} diff --git a/src/Identity/Request/CreateAttributeDefinitionRequest.php b/src/Identity/Request/CreateAttributeDefinitionRequest.php new file mode 100644 index 0000000..794d086 --- /dev/null +++ b/src/Identity/Request/CreateAttributeDefinitionRequest.php @@ -0,0 +1,33 @@ +name, + type: $this->type, + listOrder: $this->order, + defaultValue: $this->defaultValue, + required: $this->required, + tableName: $this->tableName, + ); + } +} diff --git a/src/Identity/Request/UpdateAttributeDefinitionRequest.php b/src/Identity/Request/UpdateAttributeDefinitionRequest.php new file mode 100644 index 0000000..7313a90 --- /dev/null +++ b/src/Identity/Request/UpdateAttributeDefinitionRequest.php @@ -0,0 +1,33 @@ +name, + type: $this->type, + listOrder: $this->order, + defaultValue: $this->defaultValue, + required: $this->required, + tableName: $this->tableName, + ); + } +} diff --git a/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php b/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php new file mode 100644 index 0000000..2c13171 --- /dev/null +++ b/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php @@ -0,0 +1,39 @@ + $object->getId(), + 'name' => $object->getName(), + 'type' => $object->getType(), + 'list_order' => $object->getListOrder(), + 'default_value' => $object->getDefaultValue(), + 'required' => $object->isRequired(), + 'table_name' => $object->getTableName(), + ]; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof AdminAttributeDefinition; + } +} diff --git a/src/Identity/Serializer/AdminAttributeValueNormalizer.php b/src/Identity/Serializer/AdminAttributeValueNormalizer.php new file mode 100644 index 0000000..abdc4eb --- /dev/null +++ b/src/Identity/Serializer/AdminAttributeValueNormalizer.php @@ -0,0 +1,41 @@ + $this->adminNormalizer->normalize($object->getAdministrator()), + 'definition' => $this->definitionNormalizer->normalize($object->getAttributeDefinition()), + 'value' => $object->getValue() ?? $object->getAttributeDefinition()->getDefaultValue(), + ]; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof AdminAttributeValue; + } +} diff --git a/src/Subscription/Controller/SubscriberAttributeValueController.php b/src/Subscription/Controller/SubscriberAttributeValueController.php index 1ad4ad5..68b355c 100644 --- a/src/Subscription/Controller/SubscriberAttributeValueController.php +++ b/src/Subscription/Controller/SubscriberAttributeValueController.php @@ -265,7 +265,7 @@ public function getPaginated( #[Route('/{subscriberId}/{definitionId}', name: 'get_subscriber_attribute', methods: ['GET'])] #[OA\Get( - path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', + path: '/subscribers/attribute-values/{subscriberId}/{definitionId}', description: 'Returns a single attribute.', summary: 'Gets subscriber attribute.', tags: ['subscriber-attributes'], @@ -328,7 +328,7 @@ public function getAttributeDefinition( if (!$definition || !$subscriber) { throw $this->createNotFoundException('Subscriber attribute not found.'); } - $attribute = $this->attributeManager->getSubscriberAttribute($definition->getId(), $subscriber->getId()); + $attribute = $this->attributeManager->getSubscriberAttribute($subscriber->getId(), $definition->getId()); $this->attributeManager->delete($attribute); return $this->json( From 84aebe1a5a755089cfdb8c285ecbf359b871c6a5 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sun, 18 May 2025 18:12:51 +0400 Subject: [PATCH 12/21] ISSUE-345: tests + swagger --- .../AdminAttributeDefinitionController.php | 8 +- .../AdminAttributeValueController.php | 4 +- .../OpenApi/SwaggerSchemasResponse.php | 22 +++ ...AdminAttributeDefinitionControllerTest.php | 129 ++++++++++++ .../AdminAttributeValueControllerTest.php | 185 ++++++++++++++++++ .../Fixtures/AdminAttributeDefinition.csv | 14 ++ .../AdminAttributeDefinitionFixture.php | 52 +++++ .../Identity/Fixtures/AdminAttributeValue.csv | 7 + .../Fixtures/AdminAttributeValueFixture.php | 72 +++++++ .../CreateAttributeDefinitionRequestTest.php | 49 +++++ .../UpdateAttributeDefinitionRequestTest.php | 49 +++++ ...AdminAttributeDefinitionNormalizerTest.php | 56 ++++++ .../AdminAttributeValueNormalizerTest.php | 103 ++++++++++ 13 files changed, 744 insertions(+), 6 deletions(-) create mode 100644 tests/Integration/Identity/Controller/AdminAttributeDefinitionControllerTest.php create mode 100644 tests/Integration/Identity/Controller/AdminAttributeValueControllerTest.php create mode 100644 tests/Integration/Identity/Fixtures/AdminAttributeDefinition.csv create mode 100644 tests/Integration/Identity/Fixtures/AdminAttributeDefinitionFixture.php create mode 100644 tests/Integration/Identity/Fixtures/AdminAttributeValue.csv create mode 100644 tests/Integration/Identity/Fixtures/AdminAttributeValueFixture.php create mode 100644 tests/Unit/Identity/Request/CreateAttributeDefinitionRequestTest.php create mode 100644 tests/Unit/Identity/Request/UpdateAttributeDefinitionRequestTest.php create mode 100644 tests/Unit/Identity/Serializer/AdminAttributeDefinitionNormalizerTest.php create mode 100644 tests/Unit/Identity/Serializer/AdminAttributeValueNormalizerTest.php diff --git a/src/Identity/Controller/AdminAttributeDefinitionController.php b/src/Identity/Controller/AdminAttributeDefinitionController.php index 01c3997..b020834 100644 --- a/src/Identity/Controller/AdminAttributeDefinitionController.php +++ b/src/Identity/Controller/AdminAttributeDefinitionController.php @@ -76,7 +76,7 @@ public function __construct( new OA\Response( response: 201, description: 'Success', - content: new OA\JsonContent(ref: '#/components/schemas/AttributeDefinition') + content: new OA\JsonContent(ref: '#/components/schemas/AdminAttributeDefinition') ), new OA\Response( response: 403, @@ -144,7 +144,7 @@ public function create(Request $request): JsonResponse new OA\Response( response: 200, description: 'Success', - content: new OA\JsonContent(ref: '#/components/schemas/AttributeDefinition') + content: new OA\JsonContent(ref: '#/components/schemas/AdminAttributeDefinition') ), new OA\Response( response: 403, @@ -272,7 +272,7 @@ public function delete( new OA\Property( property: 'items', type: 'array', - items: new OA\Items(ref: '#/components/schemas/AttributeDefinition') + items: new OA\Items(ref: '#/components/schemas/AdminAttributeDefinition') ), new OA\Property(property: 'pagination', ref: '#/components/schemas/CursorPagination') ], @@ -326,7 +326,7 @@ public function getPaginated(Request $request): JsonResponse new OA\Response( response: 200, description: 'Success', - content: new OA\JsonContent(ref: '#/components/schemas/AttributeDefinition') + content: new OA\JsonContent(ref: '#/components/schemas/AdminAttributeDefinition') ), new OA\Response( response: 403, diff --git a/src/Identity/Controller/AdminAttributeValueController.php b/src/Identity/Controller/AdminAttributeValueController.php index 8f2afec..45decbe 100644 --- a/src/Identity/Controller/AdminAttributeValueController.php +++ b/src/Identity/Controller/AdminAttributeValueController.php @@ -123,7 +123,7 @@ public function createOrUpdate( #[Route('/{adminId}/{definitionId}', name: 'delete_admin_attribute', methods: ['DELETE'])] #[OA\Delete( - path: '/administrators/attribute-values/{admin}/{definitionId}', + path: '/administrators/attribute-values/{adminId}/{definitionId}', description: 'Deletes a single admin attribute.', summary: 'Deletes an attribute.', tags: ['admin-attributes'], @@ -250,7 +250,7 @@ public function getPaginated( ): JsonResponse { $this->requireAuthentication($request); if (!$admin) { - $this->createNotFoundException('Administrator not found.'); + throw $this->createNotFoundException('Administrator not found.'); } $filter = (new AdminAttributeValueFilter())->setAdminId($admin->getId()); diff --git a/src/Identity/OpenApi/SwaggerSchemasResponse.php b/src/Identity/OpenApi/SwaggerSchemasResponse.php index 603c40f..b5fe0b7 100644 --- a/src/Identity/OpenApi/SwaggerSchemasResponse.php +++ b/src/Identity/OpenApi/SwaggerSchemasResponse.php @@ -22,6 +22,28 @@ ], type: 'object' )] +#[OA\Schema( + schema: 'AdminAttributeDefinition', + 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: 'checkbox'), + 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: 'list_attributes'), + ], + type: 'object' +)] +#[OA\Schema( + schema: 'AdminAttributeValue', + properties: [ + new OA\Property(property: 'administrator', ref: '#/components/schemas/Administrator'), + new OA\Property(property: 'definition', ref: '#/components/schemas/AttributeDefinition'), + new OA\Property(property: 'value', type: 'string', example: 'United States'), + ], + type: 'object' +)] class SwaggerSchemasResponse { } diff --git a/tests/Integration/Identity/Controller/AdminAttributeDefinitionControllerTest.php b/tests/Integration/Identity/Controller/AdminAttributeDefinitionControllerTest.php new file mode 100644 index 0000000..dfa6618 --- /dev/null +++ b/tests/Integration/Identity/Controller/AdminAttributeDefinitionControllerTest.php @@ -0,0 +1,129 @@ +definitionRepository = self::getContainer()->get(AdminAttributeDefinitionRepository::class); + } + + public function testControllerIsAvailableViaContainer(): void + { + self::assertInstanceOf( + AdminAttributeDefinitionController::class, + self::getContainer()->get(AdminAttributeDefinitionController::class) + ); + } + + public function testCreateAttributeDefinitionWithValidDataReturnsCreated(): void + { + $this->authenticatedJsonRequest('post', '/api/v2/administrators/attributes', [], [], [], json_encode([ + 'name' => 'Test Attribute', + 'type' => 'text', + 'order' => 1, + 'defaultValue' => 'default', + 'required' => true, + 'tableName' => 'test_table', + ])); + + $this->assertHttpCreated(); + $data = $this->getDecodedJsonResponseContent(); + self::assertSame('Test Attribute', $data['name']); + self::assertSame('text', $data['type']); + self::assertSame(1, $data['list_order']); + self::assertSame('default', $data['default_value']); + self::assertTrue($data['required']); + self::assertSame('test_table', $data['table_name']); + } + + public function testUpdateAttributeDefinitionReturnsOk(): void + { + $this->loadFixtures([AdminAttributeDefinitionFixture::class]); + $id = 2; + + $this->authenticatedJsonRequest('put', '/api/v2/administrators/attributes/' . $id, [], [], [], json_encode([ + 'name' => 'Updated Attribute', + 'type' => 'checkbox', + 'required' => true, + ])); + + $this->assertHttpOkay(); + $data = $this->getDecodedJsonResponseContent(); + self::assertSame('Updated Attribute', $data['name']); + self::assertSame('checkbox', $data['type']); + self::assertTrue($data['required']); + } + + public function testDeleteAttributeDefinitionReturnsNoContent(): void + { + $this->loadFixtures([AdminAttributeDefinitionFixture::class]); + $id = 3; + + $this->authenticatedJsonRequest('delete', '/api/v2/administrators/attributes/' . $id); + $this->assertHttpNoContent(); + + self::assertNull($this->definitionRepository->find($id)); + } + + public function testGetPaginatedReturnsOk(): void + { + $this->loadFixtures([AdminAttributeDefinitionFixture::class]); + + $this->authenticatedJsonRequest('get', '/api/v2/administrators/attributes'); + $this->assertHttpOkay(); + $data = $this->getDecodedJsonResponseContent(); + self::assertArrayHasKey('items', $data); + self::assertArrayHasKey('pagination', $data); + self::assertGreaterThanOrEqual(2, count($data['items'])); + } + + public function testGetAttributeDefinitionReturnsData(): void + { + $this->loadFixtures([AdminAttributeDefinitionFixture::class]); + $id = 6; + + $this->authenticatedJsonRequest('get', '/api/v2/administrators/attributes/' . $id); + $this->assertHttpOkay(); + $data = $this->getDecodedJsonResponseContent(); + self::assertSame('Test Get Attribute', $data['name']); + self::assertSame('text', $data['type']); + } + + public function testGetAttributeDefinitionNotFound(): void + { + $this->authenticatedJsonRequest('get', '/api/v2/administrators/attributes/999999'); + $this->assertHttpNotFound(); + } + + public function testCreateAttributeDefinitionWithInvalidJsonReturns400(): void + { + $this->authenticatedJsonRequest('post', '/api/v2/administrators/attributes', [], [], [], 'not json'); + $this->assertHttpBadRequest(); + } + + public function testCreateAttributeDefinitionWithMissingFieldsReturns422(): void + { + $this->authenticatedJsonRequest('post', '/api/v2/administrators/attributes', [], [], [], json_encode([])); + $this->assertHttpUnprocessableEntity(); + } + + public function testUpdateAttributeDefinitionWithInvalidIdReturns404(): void + { + $this->authenticatedJsonRequest('put', '/api/v2/administrators/attributes/999999', [], [], [], json_encode([ + 'name' => 'Updated Name' + ])); + $this->assertHttpNotFound(); + } +} diff --git a/tests/Integration/Identity/Controller/AdminAttributeValueControllerTest.php b/tests/Integration/Identity/Controller/AdminAttributeValueControllerTest.php new file mode 100644 index 0000000..f350315 --- /dev/null +++ b/tests/Integration/Identity/Controller/AdminAttributeValueControllerTest.php @@ -0,0 +1,185 @@ +get(AdminAttributeValueController::class) + ); + } + + public function testCreateOrUpdateAttributeValueWithValidDataReturnsCreated(): void + { + $this->loadFixtures([AdministratorFixture::class, AdminAttributeDefinitionFixture::class]); + $definitionId = 1; + $adminId = 1; + + $this->authenticatedJsonRequest( + 'post', + '/api/v2/administrators/attribute-values/' . $adminId . '/' . $definitionId, + [], + [], + [], + json_encode(['value' => 'test value']) + ); + + $this->assertHttpCreated(); + $data = $this->getDecodedJsonResponseContent(); + self::assertSame('test value', $data['value']); + self::assertSame($definitionId, $data['definition']['id']); + self::assertSame($adminId, $data['administrator']['id']); + } + + public function testUpdateAttributeValueReturnsOk(): void + { + $this->loadFixtures([ + AdministratorFixture::class, + AdminAttributeDefinitionFixture::class, + AdminAttributeValueFixture::class + ]); + $definitionId = 7; + $adminId = 1; + + $this->authenticatedJsonRequest( + 'post', + '/api/v2/administrators/attribute-values/' . $adminId . '/' . $definitionId, + [], + [], + [], + json_encode(['value' => 'updated value']) + ); + + $this->assertHttpCreated(); + $data = $this->getDecodedJsonResponseContent(); + self::assertSame('updated value', $data['value']); + } + + public function testDeleteAttributeValueReturnsNoContent(): void + { + $this->loadFixtures([ + AdministratorFixture::class, + AdminAttributeDefinitionFixture::class, + AdminAttributeValueFixture::class + ]); + $definitionId = 8; + $adminId = 1; + + $this->authenticatedJsonRequest( + 'delete', + '/api/v2/administrators/attribute-values/' . $adminId . '/' . $definitionId + ); + $this->assertHttpNoContent(); + } + + public function testGetPaginatedReturnsOk(): void + { + $this->loadFixtures([ + AdministratorFixture::class, + AdminAttributeDefinitionFixture::class, + AdminAttributeValueFixture::class + ]); + $adminId = 1; + + $this->authenticatedJsonRequest('get', '/api/v2/administrators/attribute-values/' . $adminId); + $this->assertHttpOkay(); + $data = $this->getDecodedJsonResponseContent(); + self::assertArrayHasKey('items', $data); + self::assertArrayHasKey('pagination', $data); + self::assertGreaterThanOrEqual(2, count($data['items'])); + } + + public function testGetAttributeValueReturnsData(): void + { + $this->loadFixtures([ + AdministratorFixture::class, + AdminAttributeDefinitionFixture::class, + AdminAttributeValueFixture::class + ]); + $definitionId = 11; + $adminId = 1; + + $this->authenticatedJsonRequest( + 'get', + '/api/v2/administrators/attribute-values/' . $adminId . '/' . $definitionId + ); + $this->assertHttpOkay(); + $data = $this->getDecodedJsonResponseContent(); + self::assertSame('test get value', $data['value']); + } + + public function testGetAttributeValueNotFound(): void + { + $this->loadFixtures([AdministratorFixture::class]); + $adminId = 1; + + $this->authenticatedJsonRequest( + 'get', + '/api/v2/administrators/attribute-values/' . $adminId . '/999999' + ); + $this->assertHttpNotFound(); + } + + public function testCreateAttributeValueWithInvalidJsonReturns400(): void + { + $this->loadFixtures([ + AdministratorFixture::class, + AdminAttributeDefinitionFixture::class + ]); + $definitionId = 12; + $adminId = 1; + + $this->authenticatedJsonRequest( + 'post', + '/api/v2/administrators/attribute-values/' . $adminId . '/' . $definitionId, + [], + [], + [], + 'not json' + ); + $this->assertHttpBadRequest(); + } + + public function testCreateAttributeValueWithInvalidDefinitionIdReturns404(): void + { + $this->loadFixtures([AdministratorFixture::class]); + $adminId = 1; + + $this->authenticatedJsonRequest( + 'post', + '/api/v2/administrators/attribute-values/' . $adminId . '/999999', + [], + [], + [], + json_encode(['value' => 'test value']) + ); + $this->assertHttpNotFound(); + } + + public function testCreateAttributeValueWithInvalidAdminIdReturns404(): void + { + $this->loadFixtures([AdminAttributeDefinitionFixture::class]); + $definitionId = 13; + + $this->authenticatedJsonRequest( + 'post', + '/api/v2/administrators/attribute-values/999999/' . $definitionId, + [], + [], + [], + json_encode(['value' => 'test value']) + ); + $this->assertHttpNotFound(); + } +} diff --git a/tests/Integration/Identity/Fixtures/AdminAttributeDefinition.csv b/tests/Integration/Identity/Fixtures/AdminAttributeDefinition.csv new file mode 100644 index 0000000..52c942a --- /dev/null +++ b/tests/Integration/Identity/Fixtures/AdminAttributeDefinition.csv @@ -0,0 +1,14 @@ +id,name,type,list_order,default_value,required,table_name +1,"Test Attribute","text",1,"default",1,"test_table" +2,"Original Attribute","text",2,"",0,"" +3,"To Be Deleted","text",3,"",0,"" +4,"Attribute 1","text",4,"",0,"" +5,"Attribute 2","text",5,"",0,"" +6,"Test Get Attribute","text",6,"",0,"" +7,"Update Test Attribute","text",7,"",0,"" +8,"Delete Test Attribute","text",8,"",0,"" +9,"Paginated Test 1","text",9,"",0,"" +10,"Paginated Test 2","text",10,"",0,"" +11,"Get Test Attribute","text",11,"",0,"" +12,"Invalid JSON Test","text",12,"",0,"" +13,"Invalid Admin Test","text",13,"",0,"" diff --git a/tests/Integration/Identity/Fixtures/AdminAttributeDefinitionFixture.php b/tests/Integration/Identity/Fixtures/AdminAttributeDefinitionFixture.php new file mode 100644 index 0000000..f03bc3f --- /dev/null +++ b/tests/Integration/Identity/Fixtures/AdminAttributeDefinitionFixture.php @@ -0,0 +1,52 @@ +setSubjectId($definition, (int)$row['id']); + $definition->setType($row['type']); + $definition->setListOrder((int)$row['list_order']); + $definition->setDefaultValue($row['default_value']); + $definition->setRequired((bool)$row['required']); + $definition->setTableName($row['table_name']); + + $manager->persist($definition); + } while (true); + + fclose($handle); + } +} diff --git a/tests/Integration/Identity/Fixtures/AdminAttributeValue.csv b/tests/Integration/Identity/Fixtures/AdminAttributeValue.csv new file mode 100644 index 0000000..a900d86 --- /dev/null +++ b/tests/Integration/Identity/Fixtures/AdminAttributeValue.csv @@ -0,0 +1,7 @@ +admin_id,definition_id,value +1,1,"test value" +1,7,"original value" +1,8,"to be deleted" +1,9,"value 1" +1,10,"value 2" +1,11,"test get value" diff --git a/tests/Integration/Identity/Fixtures/AdminAttributeValueFixture.php b/tests/Integration/Identity/Fixtures/AdminAttributeValueFixture.php new file mode 100644 index 0000000..3f829be --- /dev/null +++ b/tests/Integration/Identity/Fixtures/AdminAttributeValueFixture.php @@ -0,0 +1,72 @@ +getRepository(Administrator::class); + $definitionRepository = $manager->getRepository(AdminAttributeDefinition::class); + + do { + $data = fgetcsv($handle); + if ($data === false) { + break; + } + $row = array_combine($headers, $data); + + $admin = $adminRepository->find($row['admin_id']); + if ($admin === null) { + throw new RuntimeException(sprintf('Administrator with ID %d not found.', $row['admin_id'])); + } + + $definition = $definitionRepository->find($row['definition_id']); + if ($definition === null) { + throw new RuntimeException( + sprintf('AdminAttributeDefinition with ID %d not found.', $row['definition_id']) + ); + } + + $value = new AdminAttributeValue($definition, $admin); + $value->setValue($row['value']); + + $manager->persist($value); + } while (true); + + fclose($handle); + } + + public function getDependencies(): array + { + return [ + AdministratorFixture::class, + AdminAttributeDefinitionFixture::class, + ]; + } +} diff --git a/tests/Unit/Identity/Request/CreateAttributeDefinitionRequestTest.php b/tests/Unit/Identity/Request/CreateAttributeDefinitionRequestTest.php new file mode 100644 index 0000000..8246938 --- /dev/null +++ b/tests/Unit/Identity/Request/CreateAttributeDefinitionRequestTest.php @@ -0,0 +1,49 @@ +name = 'Test Attribute'; + $request->type = 'text'; + $request->order = 5; + $request->defaultValue = 'default'; + $request->required = true; + $request->tableName = 'test_table'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(AdminAttributeDefinitionDto::class, $dto); + $this->assertEquals('Test Attribute', $dto->name); + $this->assertEquals('text', $dto->type); + $this->assertEquals(5, $dto->listOrder); + $this->assertEquals('default', $dto->defaultValue); + $this->assertTrue($dto->required); + $this->assertEquals('test_table', $dto->tableName); + } + + public function testGetDtoWithDefaultValues(): void + { + $request = new CreateAttributeDefinitionRequest(); + $request->name = 'Test Attribute'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(AdminAttributeDefinitionDto::class, $dto); + $this->assertEquals('Test Attribute', $dto->name); + $this->assertNull($dto->type); + $this->assertNull($dto->listOrder); + $this->assertNull($dto->defaultValue); + $this->assertFalse($dto->required); + $this->assertNull($dto->tableName); + } +} diff --git a/tests/Unit/Identity/Request/UpdateAttributeDefinitionRequestTest.php b/tests/Unit/Identity/Request/UpdateAttributeDefinitionRequestTest.php new file mode 100644 index 0000000..41ea27e --- /dev/null +++ b/tests/Unit/Identity/Request/UpdateAttributeDefinitionRequestTest.php @@ -0,0 +1,49 @@ +name = 'Updated Attribute'; + $request->type = 'checkbox'; + $request->order = 10; + $request->defaultValue = 'updated_default'; + $request->required = true; + $request->tableName = 'updated_table'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(AdminAttributeDefinitionDto::class, $dto); + $this->assertEquals('Updated Attribute', $dto->name); + $this->assertEquals('checkbox', $dto->type); + $this->assertEquals(10, $dto->listOrder); + $this->assertEquals('updated_default', $dto->defaultValue); + $this->assertTrue($dto->required); + $this->assertEquals('updated_table', $dto->tableName); + } + + public function testGetDtoWithDefaultValues(): void + { + $request = new UpdateAttributeDefinitionRequest(); + $request->name = 'Updated Attribute'; + + $dto = $request->getDto(); + + $this->assertInstanceOf(AdminAttributeDefinitionDto::class, $dto); + $this->assertEquals('Updated Attribute', $dto->name); + $this->assertNull($dto->type); + $this->assertNull($dto->listOrder); + $this->assertNull($dto->defaultValue); + $this->assertFalse($dto->required); + $this->assertNull($dto->tableName); + } +} diff --git a/tests/Unit/Identity/Serializer/AdminAttributeDefinitionNormalizerTest.php b/tests/Unit/Identity/Serializer/AdminAttributeDefinitionNormalizerTest.php new file mode 100644 index 0000000..f7ad295 --- /dev/null +++ b/tests/Unit/Identity/Serializer/AdminAttributeDefinitionNormalizerTest.php @@ -0,0 +1,56 @@ +createMock(AdminAttributeDefinition::class); + $definition->method('getId')->willReturn(123); + $definition->method('getName')->willReturn('test_attribute'); + $definition->method('getType')->willReturn('text'); + $definition->method('getListOrder')->willReturn(5); + $definition->method('getDefaultValue')->willReturn('default'); + $definition->method('isRequired')->willReturn(true); + $definition->method('getTableName')->willReturn('test_table'); + + $normalizer = new AdminAttributeDefinitionNormalizer(); + $data = $normalizer->normalize($definition); + + $this->assertIsArray($data); + $this->assertEquals([ + 'id' => 123, + 'name' => 'test_attribute', + 'type' => 'text', + 'list_order' => 5, + 'default_value' => 'default', + 'required' => true, + 'table_name' => 'test_table', + ], $data); + } + + public function testNormalizeWithInvalidObjectReturnsEmptyArray(): void + { + $normalizer = new AdminAttributeDefinitionNormalizer(); + $data = $normalizer->normalize(new \stdClass()); + + $this->assertIsArray($data); + $this->assertEmpty($data); + } + + public function testSupportsNormalization(): void + { + $normalizer = new AdminAttributeDefinitionNormalizer(); + + $definition = $this->createMock(AdminAttributeDefinition::class); + $this->assertTrue($normalizer->supportsNormalization($definition)); + $this->assertFalse($normalizer->supportsNormalization(new \stdClass())); + } +} diff --git a/tests/Unit/Identity/Serializer/AdminAttributeValueNormalizerTest.php b/tests/Unit/Identity/Serializer/AdminAttributeValueNormalizerTest.php new file mode 100644 index 0000000..3299cd7 --- /dev/null +++ b/tests/Unit/Identity/Serializer/AdminAttributeValueNormalizerTest.php @@ -0,0 +1,103 @@ +adminNormalizer = $this->createMock(AdministratorNormalizer::class); + $this->definitionNormalizer = $this->createMock(AdminAttributeDefinitionNormalizer::class); + $this->normalizer = new AdminAttributeValueNormalizer( + $this->definitionNormalizer, + $this->adminNormalizer + ); + } + + public function testNormalizeReturnsExpectedArray(): void + { + $admin = $this->createMock(Administrator::class); + $definition = $this->createMock(AdminAttributeDefinition::class); + $definition->method('getDefaultValue')->willReturn('default_value'); + + $attributeValue = $this->createMock(AdminAttributeValue::class); + $attributeValue->method('getAdministrator')->willReturn($admin); + $attributeValue->method('getAttributeDefinition')->willReturn($definition); + $attributeValue->method('getValue')->willReturn('test_value'); + + $this->adminNormalizer->method('normalize')->willReturn(['id' => 1, 'login_name' => 'admin']); + $this->definitionNormalizer->method('normalize')->willReturn(['id' => 2, 'name' => 'test_attribute']); + + $data = $this->normalizer->normalize($attributeValue); + + $this->assertIsArray($data); + $this->assertEquals([ + 'administrator' => ['id' => 1, 'login_name' => 'admin'], + 'definition' => ['id' => 2, 'name' => 'test_attribute'], + 'value' => 'test_value', + ], $data); + } + + public function testNormalizeUsesDefaultValueWhenValueIsNull(): void + { + $admin = $this->createMock(Administrator::class); + $definition = $this->createMock(AdminAttributeDefinition::class); + $definition->method('getDefaultValue')->willReturn('default_value'); + + $attributeValue = $this->createMock(AdminAttributeValue::class); + $attributeValue->method('getAdministrator')->willReturn($admin); + $attributeValue->method('getAttributeDefinition')->willReturn($definition); + $attributeValue->method('getValue')->willReturn(null); + + $this->adminNormalizer->method('normalize')->willReturn(['id' => 1, 'login_name' => 'admin']); + $this->definitionNormalizer->method('normalize')->willReturn(['id' => 2, 'name' => 'test_attribute']); + + $data = $this->normalizer->normalize($attributeValue); + + $this->assertIsArray($data); + $this->assertEquals([ + 'administrator' => ['id' => 1, 'login_name' => 'admin'], + 'definition' => ['id' => 2, 'name' => 'test_attribute'], + 'value' => 'default_value', + ], $data); + } + + public function testNormalizeWithInvalidObjectReturnsEmptyArray(): void + { + $data = $this->normalizer->normalize(new \stdClass()); + + $this->assertIsArray($data); + $this->assertEmpty($data); + } + + public function testSupportsNormalization(): void + { + $attributeValue = $this->createMock(AdminAttributeValue::class); + $this->assertTrue($this->normalizer->supportsNormalization($attributeValue)); + $this->assertFalse($this->normalizer->supportsNormalization(new \stdClass())); + } +} From 64d2b6f6501f9a05f887d0e77559e2c944ede9db Mon Sep 17 00:00:00 2001 From: Tatevik Date: Mon, 19 May 2025 20:57:48 +0400 Subject: [PATCH 13/21] ISSUE-345: refactor --- .../AdminAttributeDefinitionController.php | 38 +++++------------- .../AdminAttributeValueController.php | 20 +++++++--- .../Controller/AdministratorController.php | 12 +++--- src/Identity/Controller/SessionController.php | 10 ++--- .../OpenApi/SwaggerSchemasRequest.php | 13 ++++++ .../Controller/CampaignController.php | 12 +++--- .../Controller/TemplateController.php | 10 ++--- .../Controller/ListMembersController.php | 6 +-- ...ubscriberAttributeDefinitionController.php | 40 +++++-------------- .../SubscriberAttributeValueController.php | 25 +++++++++--- .../Controller/SubscriberController.php | 31 ++++---------- .../Controller/SubscriberListController.php | 10 ++--- .../Controller/SubscriptionController.php | 6 +-- .../OpenApi/SwaggerSchemasRequest.php | 36 +++++++++++++++++ 14 files changed, 143 insertions(+), 126 deletions(-) diff --git a/src/Identity/Controller/AdminAttributeDefinitionController.php b/src/Identity/Controller/AdminAttributeDefinitionController.php index b020834..3277b89 100644 --- a/src/Identity/Controller/AdminAttributeDefinitionController.php +++ b/src/Identity/Controller/AdminAttributeDefinitionController.php @@ -20,7 +20,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -#[Route('/administrators/attributes')] +#[Route('/administrators/attributes', name: 'admin_attribute_definition_')] class AdminAttributeDefinitionController extends BaseController { private AdminAttributeDefinitionManager $definitionManager; @@ -40,7 +40,7 @@ public function __construct( $this->paginatedDataProvider = $paginatedDataProvider; } - #[Route('', name: 'create_admin_attribute_definition', methods: ['POST'])] + #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/administrators/attributes', description: 'Returns created admin attribute definition.', @@ -48,17 +48,7 @@ public function __construct( requestBody: new OA\RequestBody( description: 'Pass parameters to create admin attribute.', required: true, - content: new OA\JsonContent( - 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: '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'), - ] - ) + content: new OA\JsonContent(ref: '#/components/schemas/CreateAdminAttributeDefinitionRequest') ), tags: ['admin-attributes'], parameters: [ @@ -103,7 +93,7 @@ public function create(Request $request): JsonResponse return $this->json($json, Response::HTTP_CREATED); } - #[Route('/{definitionId}', name: 'update_admin_attribute_definition', methods: ['PUT'])] + #[Route('/{definitionId}', name: 'update', requirements: ['definitionId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/administrators/attributes/{definitionId}', description: 'Returns updated admin attribute definition.', @@ -111,17 +101,7 @@ public function create(Request $request): JsonResponse requestBody: new OA\RequestBody( description: 'Pass parameters to update admin attribute.', required: true, - content: new OA\JsonContent( - 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: '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'), - ] - ) + content: new OA\JsonContent(ref: '#/components/schemas/CreateAdminAttributeDefinitionRequest') ), tags: ['admin-attributes'], parameters: [ @@ -179,7 +159,7 @@ public function update( return $this->json($json, Response::HTTP_OK); } - #[Route('/{definitionId}', name: 'delete_admin_attribute_definition', methods: ['DELETE'])] + #[Route('/{definitionId}', name: 'delete', requirements: ['definitionId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/administrators/attributes/{definitionId}', description: 'Deletes a single admin attribute definition.', @@ -232,11 +212,11 @@ public function delete( return $this->json(null, Response::HTTP_NO_CONTENT); } - #[Route('', name: 'get_admin_attribute_definitions', methods: ['GET'])] + #[Route('', name: 'get_lists', methods: ['GET'])] #[OA\Get( path: '/administrators/attributes', description: 'Returns a JSON list of all admin attribute definitions.', - summary: 'Gets a list of all DMIN attribute definitions.', + summary: 'Gets a list of all admin attribute definitions.', tags: ['admin-attributes'], parameters: [ new OA\Parameter( @@ -300,7 +280,7 @@ public function getPaginated(Request $request): JsonResponse ); } - #[Route('/{definitionId}', name: 'get_admin_attribute_definition', methods: ['GET'])] + #[Route('/{definitionId}', name: 'get_one', requirements: ['definitionId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/administrators/attributes/{definitionId}', description: 'Returns a single attribute with specified ID.', diff --git a/src/Identity/Controller/AdminAttributeValueController.php b/src/Identity/Controller/AdminAttributeValueController.php index 45decbe..d410fce 100644 --- a/src/Identity/Controller/AdminAttributeValueController.php +++ b/src/Identity/Controller/AdminAttributeValueController.php @@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -#[Route('/administrators/attribute-values')] +#[Route('/administrators/attribute-values', name: 'admin_attribute_value_')] class AdminAttributeValueController extends BaseController { private AdminAttributeManager $attributeManager; @@ -41,7 +41,12 @@ public function __construct( $this->paginatedDataProvider = $paginatedDataProvider; } - #[Route('/{adminId}/{definitionId}', name: 'create_admin_attribute_value', methods: ['POST', 'PUT'])] + #[Route( + path: '/{adminId}/{definitionId}', + name: 'create', + requirements: ['adminId' => '\d+', 'definitionId' => '\d+'], + methods: ['POST', 'PUT'], + )] #[OA\Post( path: '/administrators/attribute-values/{adminId}/{definitionId}', description: 'Returns created/updated admin attribute.', @@ -121,7 +126,12 @@ public function createOrUpdate( return $this->json($json, Response::HTTP_CREATED); } - #[Route('/{adminId}/{definitionId}', name: 'delete_admin_attribute', methods: ['DELETE'])] + #[Route( + path: '/{adminId}/{definitionId}', + name: 'delete', + requirements: ['adminId' => '\d+', 'definitionId' => '\d+'], + methods: ['DELETE'], + )] #[OA\Delete( path: '/administrators/attribute-values/{adminId}/{definitionId}', description: 'Deletes a single admin attribute.', @@ -185,7 +195,7 @@ public function delete( return $this->json(null, Response::HTTP_NO_CONTENT); } - #[Route('/{adminId}', name: 'get_admin_attribute_list', methods: ['GET'])] + #[Route('/{adminId}', name: 'get__list', requirements: ['adminId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/administrators/attribute-values/{adminId}', description: 'Returns a JSON list of all admin attributes.', @@ -266,7 +276,7 @@ public function getPaginated( ); } - #[Route('/{adminId}/{definitionId}', name: 'get_admin_attribute', methods: ['GET'])] + #[Route('/{adminId}/{definitionId}', name: 'get_one', methods: ['GET'])] #[OA\Get( path: '/administrators/attribute-values/{adminId}/{definitionId}', description: 'Returns a single attribute.', diff --git a/src/Identity/Controller/AdministratorController.php b/src/Identity/Controller/AdministratorController.php index e99b5e4..8adaad8 100644 --- a/src/Identity/Controller/AdministratorController.php +++ b/src/Identity/Controller/AdministratorController.php @@ -23,7 +23,7 @@ /** * This controller provides CRUD operations for Administrator entities. */ -#[Route('/administrators')] +#[Route('/administrators', name: 'admin_')] class AdministratorController extends BaseController { private AdministratorManager $administratorManager; @@ -43,7 +43,7 @@ public function __construct( $this->paginatedProvider = $paginatedProvider; } - #[Route('', name: 'get_administrators', methods: ['GET'])] + #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/administrators', description: 'Get list of administrators.', @@ -106,7 +106,7 @@ public function getAdministrators(Request $request): JsonResponse ); } - #[Route('', name: 'create_administrator', methods: ['POST'])] + #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/administrators', description: 'Create a new administrator.', @@ -145,7 +145,7 @@ public function createAdministrator( return $this->json($json, Response::HTTP_CREATED); } - #[Route('/{administratorId}', name: 'get_administrator', methods: ['GET'])] + #[Route('/{administratorId}', name: 'get_one', requirements: ['administratorId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/administrators/{administratorId}', description: 'Get administrator by ID.', @@ -186,7 +186,7 @@ public function getAdministrator( return $this->json($json, Response::HTTP_OK); } - #[Route('/{administratorId}', name: 'update_administrator', methods: ['PUT'])] + #[Route('/{administratorId}', name: 'update', requirements: ['administratorId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/administrators/{administratorId}', description: 'Update an administrator.', @@ -233,7 +233,7 @@ public function updateAdministrator( return $this->json($this->normalizer->normalize($administrator), Response::HTTP_OK); } - #[Route('/{administratorId}', name: 'delete_administrator', methods: ['DELETE'])] + #[Route('/{administratorId}', name: 'delete', requirements: ['administratorId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/administrators/{administratorId}', description: 'Delete an administrator.', diff --git a/src/Identity/Controller/SessionController.php b/src/Identity/Controller/SessionController.php index 6b55676..5ea1c9b 100644 --- a/src/Identity/Controller/SessionController.php +++ b/src/Identity/Controller/SessionController.php @@ -25,7 +25,7 @@ * @author Oliver Klee * @author Tatevik Grigoryan */ -#[Route('/sessions')] +#[Route('/sessions', name: 'session_')] class SessionController extends BaseController { private SessionManager $sessionManager; @@ -40,7 +40,7 @@ public function __construct( $this->sessionManager = $sessionManager; } - #[Route('', name: 'create_session', methods: ['POST'])] + #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/sessions', description: 'Given valid login data, this will generate a login token that will be valid for 1 hour.', @@ -92,8 +92,8 @@ public function createSession( /** @var CreateSessionRequest $createSessionRequest */ $createSessionRequest = $this->validator->validate($request, CreateSessionRequest::class); $token = $this->sessionManager->createSession( - $createSessionRequest->loginName, - $createSessionRequest->password + loginName:$createSessionRequest->loginName, + password: $createSessionRequest->password ); $json = $normalizer->normalize($token, 'json'); @@ -108,7 +108,7 @@ public function createSession( * * @throws AccessDeniedHttpException */ - #[Route('/{sessionId}', name: 'delete_session', methods: ['DELETE'])] + #[Route('/{sessionId}', name: 'delete', requirements: ['sessionId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/sessions/{sessionId}', description: 'Delete the session passed as a parameter.', diff --git a/src/Identity/OpenApi/SwaggerSchemasRequest.php b/src/Identity/OpenApi/SwaggerSchemasRequest.php index 3493a47..bc1f096 100644 --- a/src/Identity/OpenApi/SwaggerSchemasRequest.php +++ b/src/Identity/OpenApi/SwaggerSchemasRequest.php @@ -71,6 +71,19 @@ ], type: 'object' )] +#[OA\Schema( + schema: 'CreateAdminAttributeDefinitionRequest', + 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: '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' +)] class SwaggerSchemasRequest { } diff --git a/src/Messaging/Controller/CampaignController.php b/src/Messaging/Controller/CampaignController.php index ff28c8e..3f254e6 100644 --- a/src/Messaging/Controller/CampaignController.php +++ b/src/Messaging/Controller/CampaignController.php @@ -26,7 +26,7 @@ * * @author Tatevik Grigoryan */ -#[Route('/campaigns')] +#[Route('/campaigns', name: 'campaign_')] class CampaignController extends BaseController { private MessageNormalizer $normalizer; @@ -46,7 +46,7 @@ public function __construct( $this->paginatedProvider = $paginatedProvider; } - #[Route('', name: 'get_campaigns', methods: ['GET'])] + #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/campaigns', description: 'Returns a JSON list of all campaigns/messages.', @@ -112,7 +112,7 @@ public function getMessages(Request $request): JsonResponse ); } - #[Route('/{messageId}', name: 'get_campaign', methods: ['GET'])] + #[Route('/{messageId}', name: 'get_one', requirements: ['messageId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/campaigns/{messageId}', description: 'Returns campaign/message by id.', @@ -162,7 +162,7 @@ public function getMessage( return $this->json($this->normalizer->normalize($message), Response::HTTP_OK); } - #[Route('', name: 'create_message', methods: ['POST'])] + #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/campaigns', description: 'Returns created message.', @@ -224,7 +224,7 @@ public function createMessage(Request $request, MessageNormalizer $normalizer): return $this->json($normalizer->normalize($data), Response::HTTP_CREATED); } - #[Route('/{messageId}', name: 'update_campaign', methods: ['PUT'])] + #[Route('/{messageId}', name: 'update', requirements: ['messageId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/campaigns/{messageId}', description: 'Updates campaign/message by id.', @@ -297,7 +297,7 @@ public function updateMessage( return $this->json($this->normalizer->normalize($data), Response::HTTP_OK); } - #[Route('/{messageId}', name: 'delete_campaign', methods: ['DELETE'])] + #[Route('/{messageId}', name: 'delete', requirements: ['messageId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/campaigns/{messageId}', description: 'Delete campaign/message by id.', diff --git a/src/Messaging/Controller/TemplateController.php b/src/Messaging/Controller/TemplateController.php index 00ceb34..ea73d93 100644 --- a/src/Messaging/Controller/TemplateController.php +++ b/src/Messaging/Controller/TemplateController.php @@ -24,7 +24,7 @@ * * @author Tatevik Grigoryan */ -#[Route('/templates')] +#[Route('/templates', name: 'template_')] class TemplateController extends BaseController { private TemplateNormalizer $normalizer; @@ -44,7 +44,7 @@ public function __construct( $this->paginatedDataProvider = $paginatedDataProvider; } - #[Route('', name: 'get_templates', methods: ['GET'])] + #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/templates', description: 'Returns a JSON list of all templates.', @@ -112,7 +112,7 @@ public function getTemplates(Request $request): JsonResponse ); } - #[Route('/{templateId}', name: 'get_template', methods: ['GET'])] + #[Route('/{templateId}', name: 'get_one', requirements: ['templateId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/templates/{templateId}', description: 'Returns template by id.', @@ -167,7 +167,7 @@ public function getTemplate( return $this->json($this->normalizer->normalize($template), Response::HTTP_OK); } - #[Route('', name: 'create_template', methods: ['POST'])] + #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/templates', description: 'Returns a JSON response of created template.', @@ -270,7 +270,7 @@ public function createTemplates(Request $request): JsonResponse ); } - #[Route('/{templateId}', name: 'delete_template', methods: ['DELETE'])] + #[Route('/{templateId}', name: 'delete', requirements: ['templateId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/templates/{templateId}', description: 'Deletes template by id.', diff --git a/src/Subscription/Controller/ListMembersController.php b/src/Subscription/Controller/ListMembersController.php index 636caee..ecdb470 100644 --- a/src/Subscription/Controller/ListMembersController.php +++ b/src/Subscription/Controller/ListMembersController.php @@ -19,7 +19,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -#[Route('/lists')] +#[Route('/lists', name: 'list_members_')] class ListMembersController extends BaseController { private SubscriberNormalizer $subscriberNormalizer; @@ -36,7 +36,7 @@ public function __construct( $this->paginatedProvider = $paginatedProvider; } - #[Route('/{listId}/subscribers', name: 'get_subscriber_from_list', methods: ['GET'])] + #[Route('/{listId}/subscribers', name: 'get_list', requirements: ['listId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/lists/{listId}/subscribers', description: 'Returns a JSON list of all subscribers for a subscriber list.', @@ -121,7 +121,7 @@ public function getListMembers( ); } - #[Route('/{listId}/subscribers/count', name: 'get_subscribers_count_from_list', methods: ['GET'])] + #[Route('/{listId}/subscribers/count', name: 'get_count', requirements: ['listId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/lists/{listId}/count', description: 'Returns a count of all subscribers in a given list.', diff --git a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php index 4dff070..bad9be3 100644 --- a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php +++ b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php @@ -20,7 +20,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -#[Route('/subscribers/attributes')] +#[Route('/subscribers/attributes', name: 'subscriber_attribute_definition_')] class SubscriberAttributeDefinitionController extends BaseController { private AttributeDefinitionManager $definitionManager; @@ -40,7 +40,7 @@ public function __construct( $this->paginatedDataProvider = $paginatedDataProvider; } - #[Route('', name: 'create_attribute_definition', methods: ['POST'])] + #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/subscriber/attributes', description: 'Returns created subscriber attribute definition.', @@ -48,17 +48,7 @@ public function __construct( requestBody: new OA\RequestBody( description: 'Pass parameters to create subscriber attribute.', required: true, - content: new OA\JsonContent( - 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: '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'), - ] - ) + content: new OA\JsonContent(ref: '#/components/schemas/CreateSubscriberAttributeDefinitionRequest') ), tags: ['subscriber-attributes'], parameters: [ @@ -103,7 +93,7 @@ public function create(Request $request): JsonResponse return $this->json($json, Response::HTTP_CREATED); } - #[Route('/{definitionId}', name: 'update_attribute_definition', methods: ['PUT'])] + #[Route('/{definitionId}', name: 'update', requirements: ['definitionId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/subscriber/attributes/{definitionId}', description: 'Returns updated subscriber attribute definition.', @@ -111,17 +101,7 @@ public function create(Request $request): JsonResponse requestBody: new OA\RequestBody( description: 'Pass parameters to update subscriber attribute.', required: true, - content: new OA\JsonContent( - 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: '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'), - ] - ) + content: new OA\JsonContent(ref: '#/components/schemas/CreateSubscriberAttributeDefinitionRequest') ), tags: ['subscriber-attributes'], parameters: [ @@ -171,15 +151,15 @@ public function update( $definitionRequest = $this->validator->validate($request, UpdateAttributeDefinitionRequest::class); $attributeDefinition = $this->definitionManager->update( - $attributeDefinition, - $definitionRequest->getDto(), + attributeDefinition: $attributeDefinition, + attributeDefinitionDto: $definitionRequest->getDto(), ); $json = $this->normalizer->normalize($attributeDefinition, 'json'); return $this->json($json, Response::HTTP_OK); } - #[Route('/{definitionId}', name: 'delete_attribute_definition', methods: ['DELETE'])] + #[Route('/{definitionId}', name: 'delete', requirements: ['definitionId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/subscriber/attributes/{definitionId}', description: 'Deletes a single subscriber attribute definition.', @@ -232,7 +212,7 @@ public function delete( return $this->json(null, Response::HTTP_NO_CONTENT); } - #[Route('', name: 'get_attribute_definitions', methods: ['GET'])] + #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/subscriber/attributes', description: 'Returns a JSON list of all subscriber attribute definitions.', @@ -300,7 +280,7 @@ public function getPaginated(Request $request): JsonResponse ); } - #[Route('/{definitionId}', name: 'get_attribute_definition', methods: ['GET'])] + #[Route('/{definitionId}', name: 'get_one', requirements: ['definitionId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/subscriber/attributes/{definitionId}', description: 'Returns a single attribute with specified ID.', diff --git a/src/Subscription/Controller/SubscriberAttributeValueController.php b/src/Subscription/Controller/SubscriberAttributeValueController.php index 68b355c..b19d990 100644 --- a/src/Subscription/Controller/SubscriberAttributeValueController.php +++ b/src/Subscription/Controller/SubscriberAttributeValueController.php @@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -#[Route('/subscribers/attribute-values')] +#[Route('/subscribers/attribute-values', name: 'subscriber_attribute_value_')] class SubscriberAttributeValueController extends BaseController { private SubscriberAttributeManager $attributeManager; @@ -41,7 +41,12 @@ public function __construct( $this->paginatedDataProvider = $paginatedDataProvider; } - #[Route('/{subscriberId}/{definitionId}', name: 'create_subscriber_attribute_value', methods: ['POST', 'PUT'])] + #[Route( + path: '/{subscriberId}/{definitionId}', + name: 'create', + requirements: ['subscriberId' => '\d+', 'definitionId' => '\d+'], + methods: ['POST', 'PUT'] + )] #[OA\Post( path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', description: 'Returns created/updated subscriber attribute.', @@ -121,7 +126,12 @@ public function createOrUpdate( return $this->json($json, Response::HTTP_CREATED); } - #[Route('/{subscriberId}/{definitionId}', name: 'delete_subscriber_attribute', methods: ['DELETE'])] + #[Route( + path: '/{subscriberId}/{definitionId}', + name: 'delete', + requirements: ['subscriberId' => '\d+', 'definitionId' => '\d+'], + methods: ['DELETE'] + )] #[OA\Delete( path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', description: 'Deletes a single subscriber attribute.', @@ -185,7 +195,7 @@ public function delete( return $this->json(null, Response::HTTP_NO_CONTENT); } - #[Route('/{subscriberId}', name: 'get_subscriber_attribute_list', methods: ['GET'])] + #[Route('/{subscriberId}', name: 'get_list', requirements: ['subscriberId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/subscribers/attribute-values/{subscriberId}', description: 'Returns a JSON list of all subscriber attributes.', @@ -263,7 +273,12 @@ public function getPaginated( ); } - #[Route('/{subscriberId}/{definitionId}', name: 'get_subscriber_attribute', methods: ['GET'])] + #[Route( + path: '/{subscriberId}/{definitionId}', + name: 'get_one', + requirements: ['subscriberId' => '\d+', 'definitionId' => '\d+'], + methods: ['GET'] + )] #[OA\Get( path: '/subscribers/attribute-values/{subscriberId}/{definitionId}', description: 'Returns a single attribute.', diff --git a/src/Subscription/Controller/SubscriberController.php b/src/Subscription/Controller/SubscriberController.php index c18cb2f..6cc85af 100644 --- a/src/Subscription/Controller/SubscriberController.php +++ b/src/Subscription/Controller/SubscriberController.php @@ -25,7 +25,7 @@ * @author Oliver Klee * @author Tatevik Grigoryan */ -#[Route('/subscribers')] +#[Route('/subscribers', name: 'subscriber_')] class SubscriberController extends BaseController { private SubscriberManager $subscriberManager; @@ -43,7 +43,7 @@ public function __construct( $this->subscriberNormalizer = $subscriberNormalizer; } - #[Route('', name: 'create_subscriber', methods: ['POST'])] + #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/subscribers', description: 'Creates a new subscriber (if there is no subscriber with the given email address yet).', @@ -51,14 +51,7 @@ public function __construct( requestBody: new OA\RequestBody( description: 'Pass session credentials', required: true, - content: new OA\JsonContent( - required: ['email'], - properties: [ - new OA\Property(property: 'email', type: 'string', format: 'string', example: 'admin@example.com'), - new OA\Property(property: 'request_confirmation', type: 'boolean', example: false), - new OA\Property(property: 'html_email', type: 'boolean', example: false), - ] - ) + content: new OA\JsonContent(ref: '#/components/schemas/CreateSubscriberRequest') ), tags: ['subscribers'], parameters: [ @@ -107,7 +100,7 @@ public function createSubscriber(Request $request): JsonResponse ); } - #[Route('/{subscriberId}', name: 'update_subscriber', requirements: ['subscriberId' => '\d+'], methods: ['PUT'])] + #[Route('/{subscriberId}', name: 'update', requirements: ['subscriberId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/subscribers/{subscriberId}', description: 'Update subscriber data by id.', @@ -115,17 +108,7 @@ public function createSubscriber(Request $request): JsonResponse requestBody: new OA\RequestBody( description: 'Pass session credentials', required: true, - content: new OA\JsonContent( - required: ['email'], - properties: [ - new OA\Property(property: 'email', type: 'string', format: 'string', example: 'admin@example.com'), - new OA\Property(property: 'confirmed', type: 'boolean', example: false), - new OA\Property(property: 'blacklisted', type: 'boolean', example: false), - new OA\Property(property: 'html_email', type: 'boolean', example: false), - new OA\Property(property: 'disabled', type: 'boolean', example: false), - new OA\Property(property: 'additional_data', type: 'string', example: 'asdf'), - ] - ) + content: new OA\JsonContent(ref: '#/components/schemas/UpdateSubscriberRequest') ), tags: ['subscribers'], parameters: [ @@ -183,7 +166,7 @@ public function updateSubscriber( return $this->json($this->subscriberNormalizer->normalize($subscriber, 'json'), Response::HTTP_OK); } - #[Route('/{subscriberId}', name: 'get_subscriber_by_id', methods: ['GET'])] + #[Route('/{subscriberId}', name: 'get_one', requirements: ['subscriberId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/subscribers/{subscriberId}', description: 'Get subscriber data by id.', @@ -232,7 +215,7 @@ public function getSubscriber(Request $request, int $subscriberId): JsonResponse return $this->json($this->subscriberNormalizer->normalize($subscriber), Response::HTTP_OK); } - #[Route('/{subscriberId}', name: 'delete_subscriber', requirements: ['subscriberId' => '\d+'], methods: ['DELETE'])] + #[Route('/{subscriberId}', name: 'delete', requirements: ['subscriberId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/subscribers/{subscriberId}', description: 'Delete subscriber by id.', diff --git a/src/Subscription/Controller/SubscriberListController.php b/src/Subscription/Controller/SubscriberListController.php index 408e5df..bc68ec6 100644 --- a/src/Subscription/Controller/SubscriberListController.php +++ b/src/Subscription/Controller/SubscriberListController.php @@ -26,7 +26,7 @@ * @author Xheni Myrtaj * @author Tatevik Grigoryan */ -#[Route('/lists')] +#[Route('/lists', name: 'subscriber_list_')] class SubscriberListController extends BaseController { private SubscriberListNormalizer $normalizer; @@ -46,7 +46,7 @@ public function __construct( $this->paginatedDataProvider = $paginatedDataProvider; } - #[Route('', name: 'get_lists', methods: ['GET'])] + #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/lists', description: 'Returns a JSON list of all subscriber lists.', @@ -110,7 +110,7 @@ public function getLists(Request $request): JsonResponse ); } - #[Route('/{listId}', name: 'get_list', methods: ['GET'])] + #[Route('/{listId}', name: 'get_one', requirements: ['listId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/lists/{listId}', description: 'Returns a single subscriber list with specified ID.', @@ -172,7 +172,7 @@ public function getList( return $this->json($this->normalizer->normalize($list), Response::HTTP_OK); } - #[Route('/{listId}', name: 'delete_list', methods: ['DELETE'])] + #[Route('/{listId}', name: 'delete', requirements: ['listId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/lists/{listId}', description: 'Deletes a single subscriber list.', @@ -226,7 +226,7 @@ public function deleteList( return $this->json(null, Response::HTTP_NO_CONTENT); } - #[Route('', name: 'create_list', methods: ['POST'])] + #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/lists', description: 'Returns created list.', diff --git a/src/Subscription/Controller/SubscriptionController.php b/src/Subscription/Controller/SubscriptionController.php index 3a78237..dcca689 100644 --- a/src/Subscription/Controller/SubscriptionController.php +++ b/src/Subscription/Controller/SubscriptionController.php @@ -23,7 +23,7 @@ * * @author Tatevik Grigoryan */ -#[Route('/lists')] +#[Route('/lists', name: 'subscription_')] class SubscriptionController extends BaseController { private SubscriptionManager $subscriptionManager; @@ -40,7 +40,7 @@ public function __construct( $this->subscriptionNormalizer = $subscriptionNormalizer; } - #[Route('/{listId}/subscribers', name: 'create_subscription', methods: ['POST'])] + #[Route('/{listId}/subscribers', name: 'create', requirements: ['listId' => '\d+'], methods: ['POST'])] #[OA\Post( path: '/lists/{listId}/subscribers', description: 'Subscribe subscriber to a list.', @@ -131,7 +131,7 @@ public function createSubscription( return $this->json($normalized, Response::HTTP_CREATED); } - #[Route('/{listId}/subscribers', name: 'delete_subscription', methods: ['DELETE'])] + #[Route('/{listId}/subscribers', name: 'delete', requirements: ['listId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/lists/{listId}/subscribers', description: 'Delete subscription.', diff --git a/src/Subscription/OpenApi/SwaggerSchemasRequest.php b/src/Subscription/OpenApi/SwaggerSchemasRequest.php index 6009711..5e7822b 100644 --- a/src/Subscription/OpenApi/SwaggerSchemasRequest.php +++ b/src/Subscription/OpenApi/SwaggerSchemasRequest.php @@ -17,6 +17,42 @@ ], type: 'object' )] +#[OA\Schema( + schema: 'CreateSubscriberAttributeDefinitionRequest', + 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: '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' +)] +#[OA\Schema( + schema: 'CreateSubscriberRequest', + required: ['email'], + properties: [ + new OA\Property(property: 'email', type: 'string', format: 'string', example: 'admin@example.com'), + new OA\Property(property: 'request_confirmation', type: 'boolean', example: false), + new OA\Property(property: 'html_email', type: 'boolean', example: false), + ], + type: 'object' +)] +#[OA\Schema( + schema: 'UpdateSubscriberRequest', + required: ['email'], + properties: [ + new OA\Property(property: 'email', type: 'string', format: 'string', example: 'admin@example.com'), + new OA\Property(property: 'confirmed', type: 'boolean', example: false), + new OA\Property(property: 'blacklisted', type: 'boolean', example: false), + new OA\Property(property: 'html_email', type: 'boolean', example: false), + new OA\Property(property: 'disabled', type: 'boolean', example: false), + new OA\Property(property: 'additional_data', type: 'string', example: 'asdf'), + ], + type: 'object' +)] class SwaggerSchemasRequest { } From 05b0f8eb77f139c315c371e95ac6d2f51f78d3de Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 21 May 2025 22:09:15 +0400 Subject: [PATCH 14/21] ISSUE-345: subscriber export/import --- config/services/managers.yml | 4 + .../Controller/SubscriberExportController.php | 79 ++++++++++ .../Controller/SubscriberImportController.php | 137 +++++++++++++++++ .../SubscriberImportControllerTest.php | 145 ++++++++++++++++++ 4 files changed, 365 insertions(+) create mode 100644 src/Subscription/Controller/SubscriberExportController.php create mode 100644 src/Subscription/Controller/SubscriberImportController.php create mode 100644 tests/Integration/Subscription/Controller/SubscriberImportControllerTest.php diff --git a/config/services/managers.yml b/config/services/managers.yml index 25dbfe9..3920028 100644 --- a/config/services/managers.yml +++ b/config/services/managers.yml @@ -35,3 +35,7 @@ services: PhpList\Core\Domain\Udentity\Service\AdministratorManager: autowire: true autoconfigure: true + + PhpList\Core\Domain\Subscription\Service\SubscriberCsvExportManager: + autowire: true + autoconfigure: true diff --git a/src/Subscription/Controller/SubscriberExportController.php b/src/Subscription/Controller/SubscriberExportController.php new file mode 100644 index 0000000..91e5d4c --- /dev/null +++ b/src/Subscription/Controller/SubscriberExportController.php @@ -0,0 +1,79 @@ +exportManager = $exportManager; + } + + #[Route('/export', name: 'csv', methods: ['GET'])] + #[OA\Get( + path: '/subscribers/export', + description: 'Export subscribers to CSV file.', + summary: 'Export subscribers', + tags: ['subscribers'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ), + new OA\Parameter( + name: 'batch_size', + description: 'Number of subscribers to process in each batch (default: 1000)', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer', default: 1000) + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\MediaType( + mediaType: 'text/csv', + schema: new OA\Schema(type: 'string', format: 'binary') + ) + ), + new OA\Response( + response: 403, + description: 'Failure', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ) + ] + )] + public function exportSubscribers(Request $request): Response + { + $this->requireAuthentication($request); + + $batchSize = (int)$request->query->get('batch_size', 1000); + + $filter = new SubscriberFilter(); + + return $this->exportManager->exportToCsv($filter, $batchSize); + } +} diff --git a/src/Subscription/Controller/SubscriberImportController.php b/src/Subscription/Controller/SubscriberImportController.php new file mode 100644 index 0000000..081e970 --- /dev/null +++ b/src/Subscription/Controller/SubscriberImportController.php @@ -0,0 +1,137 @@ +importManager = $importManager; + } + + #[Route('/import', name: 'csv', methods: ['POST'])] + #[OA\Post( + path: '/subscribers/import', + description: 'Import subscribers from CSV file.', + summary: 'Import subscribers', + requestBody: new OA\RequestBody( + required: true, + content: new OA\MediaType( + mediaType: 'multipart/form-data', + schema: new OA\Schema( + properties: [ + new OA\Property( + property: 'file', + description: 'CSV file with subscribers data', + type: 'string', + format: 'binary' + ), + new OA\Property( + property: 'request_confirmation', + description: 'Whether to request confirmation from imported subscribers', + type: 'boolean', + default: false + ), + new OA\Property( + property: 'html_email', + description: 'Whether imported subscribers prefer HTML emails', + type: 'boolean', + default: true + ) + ], + type: 'object' + ) + ) + ), + tags: ['subscribers'], + parameters: [ + new OA\Parameter( + name: 'session', + description: 'Session ID obtained from authentication', + in: 'header', + required: true, + schema: new OA\Schema(type: 'string') + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Success', + content: new OA\JsonContent( + properties: [ + new OA\Property(property: 'imported', type: 'integer'), + new OA\Property(property: 'skipped', type: 'integer'), + new OA\Property( + property: 'errors', + type: 'array', + items: new OA\Items(type: 'string') + ) + ] + ) + ), + new OA\Response( + response: 400, + description: 'Bad Request', + content: new OA\JsonContent(ref: '#/components/schemas/BadRequestResponse') + ), + new OA\Response( + response: 403, + description: 'Unauthorized', + content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse') + ) + ] + )] + public function importSubscribers(Request $request): JsonResponse + { + $this->requireAuthentication($request); + + /** @var UploadedFile|null $file */ + $file = $request->files->get('file'); + + if (!$file) { + return $this->json(['success' => false, 'message' => 'No file uploaded'], Response::HTTP_BAD_REQUEST); + } + + if ($file->getClientMimeType() !== 'text/csv' && $file->getClientOriginalExtension() !== 'csv') { + return $this->json(['success' => false, 'message' => 'File must be a CSV'], Response::HTTP_BAD_REQUEST); + } + + try { + $options = new SubscriberImportOptions(); + + $stats = $this->importManager->importFromCsv($file, $options); + + return $this->json([ + 'imported' => $stats['created'], + 'skipped' => $stats['skipped'], + 'errors' => $stats['errors'] + ]); + } catch (Exception $e) { + return $this->json([ + 'message' => $e->getMessage() + ], Response::HTTP_BAD_REQUEST); + } + } +} diff --git a/tests/Integration/Subscription/Controller/SubscriberImportControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberImportControllerTest.php new file mode 100644 index 0000000..f21cf24 --- /dev/null +++ b/tests/Integration/Subscription/Controller/SubscriberImportControllerTest.php @@ -0,0 +1,145 @@ +tempDir = sys_get_temp_dir(); + } + + public function testControllerIsAvailableViaContainer(): void + { + self::assertInstanceOf( + SubscriberImportController::class, + self::getContainer()->get(SubscriberImportController::class) + ); + } + + public function testImportSubscribersWithoutSessionKeyReturnsForbiddenStatus(): void + { + self::getClient()->request('POST', '/api/v2/subscribers/import'); + + $this->assertHttpForbidden(); + } + + public function testImportSubscribersWithoutFileReturnsBadRequestStatus(): void + { + $this->authenticatedJsonRequest('POST', '/api/v2/subscribers/import'); + + $this->assertHttpBadRequest(); + $responseContent = $this->getDecodedJsonResponseContent(); + self::assertSame(false, $responseContent['success']); + self::assertStringContainsString('No file uploaded', $responseContent['message']); + } + + public function testImportSubscribersWithNonCsvFileReturnsBadRequestStatus(): void + { + $filePath = $this->tempDir . '/test.txt'; + file_put_contents($filePath, 'This is not a CSV file'); + + $file = new UploadedFile( + $filePath, + 'test.txt', + 'text/plain', + null, + true + ); + + $this->authenticatedJsonRequest( + 'POST', + '/api/v2/subscribers/import', + [], + ['file' => $file] + ); + + $this->assertHttpBadRequest(); + $responseContent = $this->getDecodedJsonResponseContent(); + self::assertSame(false, $responseContent['success']); + self::assertStringContainsString('File must be a CSV', $responseContent['message']); + } + + public function testImportSubscribersWithValidCsvFile(): void + { + $filePath = $this->tempDir . '/subscribers.csv'; + $csvContent = "email,name\ntest@example.com,Test User\ntest2@example.com,Test User 2"; + file_put_contents($filePath, $csvContent); + + $file = new UploadedFile( + $filePath, + 'subscribers.csv', + 'text/csv', + null, + true + ); + + $this->authenticatedJsonRequest( + 'POST', + '/api/v2/subscribers/import', + [], + ['file' => $file] + ); + + $response = self::getClient()->getResponse(); + self::assertSame(Response::HTTP_OK, $response->getStatusCode()); + + $responseContent = $this->getDecodedJsonResponseContent(); + self::assertSame(true, $responseContent['success']); + self::assertArrayHasKey('imported', $responseContent); + self::assertArrayHasKey('skipped', $responseContent); + self::assertArrayHasKey('errors', $responseContent); + } + + public function testImportSubscribersWithOptions(): void + { + $filePath = $this->tempDir . '/subscribers.csv'; + $csvContent = "email,name\ntest@example.com,Test User"; + file_put_contents($filePath, $csvContent); + + $file = new UploadedFile( + $filePath, + 'subscribers.csv', + 'text/csv', + null, + true + ); + + $this->authenticatedJsonRequest( + 'POST', + '/api/v2/subscribers/import', + [ + 'request_confirmation' => 'true', + 'html_email' => 'false' + ], + ['file' => $file] + ); + + $response = self::getClient()->getResponse(); + self::assertSame(Response::HTTP_OK, $response->getStatusCode()); + + $responseContent = $this->getDecodedJsonResponseContent(); + self::assertSame(true, $responseContent['success']); + } + + public function testGetMethodIsNotAllowed(): void + { + $this->authenticatedJsonRequest('GET', '/api/v2/subscribers/import'); + + $this->assertHttpMethodNotAllowed(); + } +} From 4c3da9010b8abe06ecf6da1f209200a54b8cdbac Mon Sep 17 00:00:00 2001 From: Tatevik Date: Fri, 23 May 2025 19:47:38 +0400 Subject: [PATCH 15/21] ISSUE-345: subscriber import update --- config/services/managers.yml | 10 +++--- ...ubscriberAttributeDefinitionController.php | 2 +- .../SubscriberAttributeValueController.php | 2 +- .../Controller/SubscriberController.php | 2 +- .../Controller/SubscriberExportController.php | 6 ++-- .../Controller/SubscriberImportController.php | 31 ++++++++++--------- .../Controller/SubscriberListController.php | 2 +- .../Controller/SubscriptionController.php | 2 +- .../SubscriberImportControllerTest.php | 7 ++--- 9 files changed, 32 insertions(+), 32 deletions(-) diff --git a/config/services/managers.yml b/config/services/managers.yml index 3920028..eeb4958 100644 --- a/config/services/managers.yml +++ b/config/services/managers.yml @@ -4,7 +4,7 @@ services: autoconfigure: true public: false - PhpList\Core\Domain\Subscription\Service\SubscriberManager: + PhpList\Core\Domain\Subscription\Service\Manager\SubscriberManager: autowire: true autoconfigure: true @@ -12,11 +12,11 @@ services: autowire: true autoconfigure: true - PhpList\Core\Domain\Subscription\Service\SubscriberListManager: + PhpList\Core\Domain\Subscription\Service\Manager\SubscriberListManager: autowire: true autoconfigure: true - PhpList\Core\Domain\Subscription\Service\SubscriptionManager: + PhpList\Core\Domain\Subscription\Service\Manager\SubscriptionManager: autowire: true autoconfigure: true @@ -32,10 +32,10 @@ services: autowire: true autoconfigure: true - PhpList\Core\Domain\Udentity\Service\AdministratorManager: + PhpList\Core\Domain\Identity\Service\AdministratorManager: autowire: true autoconfigure: true - PhpList\Core\Domain\Subscription\Service\SubscriberCsvExportManager: + PhpList\Core\Domain\Subscription\Service\SubscriberCsvExporter: autowire: true autoconfigure: true diff --git a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php index bad9be3..c7db4e7 100644 --- a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php +++ b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php @@ -6,7 +6,7 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition; -use PhpList\Core\Domain\Subscription\Service\AttributeDefinitionManager; +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; diff --git a/src/Subscription/Controller/SubscriberAttributeValueController.php b/src/Subscription/Controller/SubscriberAttributeValueController.php index b19d990..a1b05a8 100644 --- a/src/Subscription/Controller/SubscriberAttributeValueController.php +++ b/src/Subscription/Controller/SubscriberAttributeValueController.php @@ -9,7 +9,7 @@ use PhpList\Core\Domain\Subscription\Model\Subscriber; use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeDefinition; use PhpList\Core\Domain\Subscription\Model\SubscriberAttributeValue; -use PhpList\Core\Domain\Subscription\Service\SubscriberAttributeManager; +use PhpList\Core\Domain\Subscription\Service\Manager\SubscriberAttributeManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Common\Controller\BaseController; use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider; diff --git a/src/Subscription/Controller/SubscriberController.php b/src/Subscription/Controller/SubscriberController.php index 6cc85af..692af09 100644 --- a/src/Subscription/Controller/SubscriberController.php +++ b/src/Subscription/Controller/SubscriberController.php @@ -6,7 +6,7 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\Subscriber; -use PhpList\Core\Domain\Subscription\Service\SubscriberManager; +use PhpList\Core\Domain\Subscription\Service\Manager\SubscriberManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Common\Controller\BaseController; use PhpList\RestBundle\Common\Validator\RequestValidator; diff --git a/src/Subscription/Controller/SubscriberExportController.php b/src/Subscription/Controller/SubscriberExportController.php index 91e5d4c..e39ddc7 100644 --- a/src/Subscription/Controller/SubscriberExportController.php +++ b/src/Subscription/Controller/SubscriberExportController.php @@ -6,7 +6,7 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\Filter\SubscriberFilter; -use PhpList\Core\Domain\Subscription\Service\SubscriberCsvExportManager; +use PhpList\Core\Domain\Subscription\Service\SubscriberCsvExporter; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Common\Controller\BaseController; use PhpList\RestBundle\Common\Validator\RequestValidator; @@ -17,12 +17,12 @@ #[Route('/subscribers', name: 'subscriber_export_')] class SubscriberExportController extends BaseController { - private SubscriberCsvExportManager $exportManager; + private SubscriberCsvExporter $exportManager; public function __construct( Authentication $authentication, RequestValidator $validator, - SubscriberCsvExportManager $exportManager + SubscriberCsvExporter $exportManager ) { parent::__construct($authentication, $validator); $this->exportManager = $exportManager; diff --git a/src/Subscription/Controller/SubscriberImportController.php b/src/Subscription/Controller/SubscriberImportController.php index 081e970..7bf31f6 100644 --- a/src/Subscription/Controller/SubscriberImportController.php +++ b/src/Subscription/Controller/SubscriberImportController.php @@ -7,7 +7,7 @@ use Exception; use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\Dto\SubscriberImportOptions; -use PhpList\Core\Domain\Subscription\Service\SubscriberCsvImportManager; +use PhpList\Core\Domain\Subscription\Service\SubscriberCsvImporter; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Common\Controller\BaseController; use PhpList\RestBundle\Common\Validator\RequestValidator; @@ -20,12 +20,12 @@ #[Route('/subscribers', name: 'subscriber_import_')] class SubscriberImportController extends BaseController { - private SubscriberCsvImportManager $importManager; + private SubscriberCsvImporter $importManager; public function __construct( Authentication $authentication, RequestValidator $validator, - SubscriberCsvImportManager $importManager + SubscriberCsvImporter $importManager ) { parent::__construct($authentication, $validator); $this->importManager = $importManager; @@ -49,16 +49,16 @@ public function __construct( format: 'binary' ), new OA\Property( - property: 'request_confirmation', - description: 'Whether to request confirmation from imported subscribers', - type: 'boolean', - default: false + property: 'list_id', + description: 'List id to add imported subscribers to', + type: 'integer', + default: null ), new OA\Property( - property: 'html_email', - description: 'Whether imported subscribers prefer HTML emails', + property: 'update_existing', + description: 'Weather to update existing subscribers or not', type: 'boolean', - default: true + default: false ) ], type: 'object' @@ -111,17 +111,18 @@ public function importSubscribers(Request $request): JsonResponse $file = $request->files->get('file'); if (!$file) { - return $this->json(['success' => false, 'message' => 'No file uploaded'], Response::HTTP_BAD_REQUEST); + return $this->json(['message' => 'No file uploaded'], Response::HTTP_BAD_REQUEST); } if ($file->getClientMimeType() !== 'text/csv' && $file->getClientOriginalExtension() !== 'csv') { - return $this->json(['success' => false, 'message' => 'File must be a CSV'], Response::HTTP_BAD_REQUEST); + return $this->json(['message' => 'File must be a CSV'], Response::HTTP_BAD_REQUEST); } try { - $options = new SubscriberImportOptions(); - - $stats = $this->importManager->importFromCsv($file, $options); + $stats = $this->importManager->importFromCsv( + $file, + new SubscriberImportOptions($request->getPayload()->getBoolean('update_existing')) + ); return $this->json([ 'imported' => $stats['created'], diff --git a/src/Subscription/Controller/SubscriberListController.php b/src/Subscription/Controller/SubscriberListController.php index bc68ec6..5215697 100644 --- a/src/Subscription/Controller/SubscriberListController.php +++ b/src/Subscription/Controller/SubscriberListController.php @@ -6,7 +6,7 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\SubscriberList; -use PhpList\Core\Domain\Subscription\Service\SubscriberListManager; +use PhpList\Core\Domain\Subscription\Service\Manager\SubscriberListManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Common\Controller\BaseController; use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider; diff --git a/src/Subscription/Controller/SubscriptionController.php b/src/Subscription/Controller/SubscriptionController.php index dcca689..fa7061e 100644 --- a/src/Subscription/Controller/SubscriptionController.php +++ b/src/Subscription/Controller/SubscriptionController.php @@ -6,7 +6,7 @@ use OpenApi\Attributes as OA; use PhpList\Core\Domain\Subscription\Model\SubscriberList; -use PhpList\Core\Domain\Subscription\Service\SubscriptionManager; +use PhpList\Core\Domain\Subscription\Service\Manager\SubscriptionManager; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Common\Controller\BaseController; use PhpList\RestBundle\Common\Validator\RequestValidator; diff --git a/tests/Integration/Subscription/Controller/SubscriberImportControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberImportControllerTest.php index f21cf24..8acd62f 100644 --- a/tests/Integration/Subscription/Controller/SubscriberImportControllerTest.php +++ b/tests/Integration/Subscription/Controller/SubscriberImportControllerTest.php @@ -44,7 +44,6 @@ public function testImportSubscribersWithoutFileReturnsBadRequestStatus(): void $this->assertHttpBadRequest(); $responseContent = $this->getDecodedJsonResponseContent(); - self::assertSame(false, $responseContent['success']); self::assertStringContainsString('No file uploaded', $responseContent['message']); } @@ -70,7 +69,6 @@ public function testImportSubscribersWithNonCsvFileReturnsBadRequestStatus(): vo $this->assertHttpBadRequest(); $responseContent = $this->getDecodedJsonResponseContent(); - self::assertSame(false, $responseContent['success']); self::assertStringContainsString('File must be a CSV', $responseContent['message']); } @@ -99,7 +97,6 @@ public function testImportSubscribersWithValidCsvFile(): void self::assertSame(Response::HTTP_OK, $response->getStatusCode()); $responseContent = $this->getDecodedJsonResponseContent(); - self::assertSame(true, $responseContent['success']); self::assertArrayHasKey('imported', $responseContent); self::assertArrayHasKey('skipped', $responseContent); self::assertArrayHasKey('errors', $responseContent); @@ -133,7 +130,9 @@ public function testImportSubscribersWithOptions(): void self::assertSame(Response::HTTP_OK, $response->getStatusCode()); $responseContent = $this->getDecodedJsonResponseContent(); - self::assertSame(true, $responseContent['success']); + self::assertArrayHasKey('imported', $responseContent); + self::assertArrayHasKey('skipped', $responseContent); + self::assertArrayHasKey('errors', $responseContent); } public function testGetMethodIsNotAllowed(): void From e032f5a18401bc2fdbbf8d6a397fd459720eed57 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Fri, 23 May 2025 20:14:53 +0400 Subject: [PATCH 16/21] ISSUE-345: subscriber export options --- config/services/normalizers.yml | 4 + config/services/validators.yml | 5 ++ .../Controller/ListMembersController.php | 2 +- .../Controller/SubscriberExportController.php | 52 +++++++++--- .../Request/SubscribersExportRequest.php | 81 +++++++++++++++++++ .../SubscribersExportRequestNormalizer.php | 37 +++++++++ .../Validator/Constraint/ListExists.php | 22 +++++ .../Constraint/ListExistsValidator.php | 43 ++++++++++ 8 files changed, 236 insertions(+), 10 deletions(-) create mode 100644 src/Subscription/Request/SubscribersExportRequest.php create mode 100644 src/Subscription/Serializer/SubscribersExportRequestNormalizer.php create mode 100644 src/Subscription/Validator/Constraint/ListExists.php create mode 100644 src/Subscription/Validator/Constraint/ListExistsValidator.php diff --git a/config/services/normalizers.yml b/config/services/normalizers.yml index 430a8ec..0420706 100644 --- a/config/services/normalizers.yml +++ b/config/services/normalizers.yml @@ -65,3 +65,7 @@ services: PhpList\RestBundle\Common\Serializer\CursorPaginationNormalizer: autowire: true + + PhpList\RestBundle\Subscription\Serializer\SubscribersExportRequestNormalizer: + tags: [ 'serializer.normalizer' ] + autowire: true diff --git a/config/services/validators.yml b/config/services/validators.yml index 6b158f5..0080f7a 100644 --- a/config/services/validators.yml +++ b/config/services/validators.yml @@ -31,3 +31,8 @@ services: autowire: true autoconfigure: true tags: [ 'validator.constraint_validator' ] + + PhpList\RestBundle\Subscription\Validator\Constraint\ListExistsValidator: + autowire: true + autoconfigure: true + tags: [ 'validator.constraint_validator' ] diff --git a/src/Subscription/Controller/ListMembersController.php b/src/Subscription/Controller/ListMembersController.php index ecdb470..86e278a 100644 --- a/src/Subscription/Controller/ListMembersController.php +++ b/src/Subscription/Controller/ListMembersController.php @@ -115,7 +115,7 @@ public function getListMembers( $request, $this->subscriberNormalizer, Subscriber::class, - (new SubscriberFilter())->setListId($list->getId()) + new SubscriberFilter($list->getId()) ), Response::HTTP_OK ); diff --git a/src/Subscription/Controller/SubscriberExportController.php b/src/Subscription/Controller/SubscriberExportController.php index e39ddc7..b679256 100644 --- a/src/Subscription/Controller/SubscriberExportController.php +++ b/src/Subscription/Controller/SubscriberExportController.php @@ -5,11 +5,11 @@ namespace PhpList\RestBundle\Subscription\Controller; use OpenApi\Attributes as OA; -use PhpList\Core\Domain\Subscription\Model\Filter\SubscriberFilter; use PhpList\Core\Domain\Subscription\Service\SubscriberCsvExporter; use PhpList\Core\Security\Authentication; use PhpList\RestBundle\Common\Controller\BaseController; use PhpList\RestBundle\Common\Validator\RequestValidator; +use PhpList\RestBundle\Subscription\Request\SubscribersExportRequest; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; @@ -43,11 +43,46 @@ public function __construct( schema: new OA\Schema(type: 'string') ), new OA\Parameter( - name: 'batch_size', - description: 'Number of subscribers to process in each batch (default: 1000)', + name: 'date_type', + description: 'What date needs to be used for filtering (any, signup, changed, changelog, subscribed)', in: 'query', required: false, - schema: new OA\Schema(type: 'integer', default: 1000) + schema: new OA\Schema( + type: 'string', + default: 'any', + enum: ['any', 'signup', 'changed', 'changelog', 'subscribed'] + ) + ), + new OA\Parameter( + name: 'list_id', + description: 'List ID from where to export', + in: 'query', + required: false, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'date_from', + description: 'Start date for filtering (format: Y-m-d)', + in: 'query', + required: false, + schema: new OA\Schema(type: 'string', format: 'date') + ), + new OA\Parameter( + name: 'date_to', + description: 'End date for filtering (format: Y-m-d)', + in: 'query', + required: false, + schema: new OA\Schema(type: 'string', format: 'date') + ), + new OA\Parameter( + name: 'columns', + description: 'Columns to include in the export (comma-separated)', + in: 'query', + required: false, + schema: new OA\Schema( + type: 'string', + default: 'id,email,confirmed,blacklisted,bounceCount,createdAt,updatedAt,uniqueId,htmlEmail,disabled,extraData' + ) ) ], responses: [ @@ -70,10 +105,9 @@ public function exportSubscribers(Request $request): Response { $this->requireAuthentication($request); - $batchSize = (int)$request->query->get('batch_size', 1000); - - $filter = new SubscriberFilter(); - - return $this->exportManager->exportToCsv($filter, $batchSize); + /** @var SubscribersExportRequest $exportRequest */ + $exportRequest = $this->validator->validate($request, SubscribersExportRequest::class); + + return $this->exportManager->exportToCsv($exportRequest->getDto()); } } diff --git a/src/Subscription/Request/SubscribersExportRequest.php b/src/Subscription/Request/SubscribersExportRequest.php new file mode 100644 index 0000000..fa05c73 --- /dev/null +++ b/src/Subscription/Request/SubscribersExportRequest.php @@ -0,0 +1,81 @@ +dateFrom); + $dateTo = new DateTimeImmutable($this->dateTo); + + return match ($this->dateType) { + 'subscribed' => [$dateFrom, $dateTo, null, null, null, null], + 'signup' => [null, null, $dateFrom, $dateTo, null, null], + 'changed' => [null, null, null, null, $dateFrom, $dateTo], + 'any', 'changelog' => [null, null, null, null, null, null], + }; + } + + public function getDto(): SubscriberFilter + { + [$subscribedFrom, $subscribedTo, $signupFrom, $signupTo, $changedFrom, $changedTo] = $this->resolveDates(); + + return new SubscriberFilter( + $this->listId ?? null, + $subscribedFrom, + $subscribedTo, + $signupFrom, + $signupTo, + $changedFrom, + $changedTo, + $this->columns + ); + } +} diff --git a/src/Subscription/Serializer/SubscribersExportRequestNormalizer.php b/src/Subscription/Serializer/SubscribersExportRequestNormalizer.php new file mode 100644 index 0000000..122ee80 --- /dev/null +++ b/src/Subscription/Serializer/SubscribersExportRequestNormalizer.php @@ -0,0 +1,37 @@ + $object->dateType, + 'list_id' => $object->listId, + 'date_from' => $object->dateFrom, + 'date_to' => $object->dateTo, + 'columns' => $object->columns, + ]; + } + + /** + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function supportsNormalization($data, string $format = null): bool + { + return $data instanceof SubscribersExportRequest; + } +} diff --git a/src/Subscription/Validator/Constraint/ListExists.php b/src/Subscription/Validator/Constraint/ListExists.php new file mode 100644 index 0000000..6811ff2 --- /dev/null +++ b/src/Subscription/Validator/Constraint/ListExists.php @@ -0,0 +1,22 @@ +mode = $mode ?? $this->mode; + $this->message = $message ?? $this->message; + } +} diff --git a/src/Subscription/Validator/Constraint/ListExistsValidator.php b/src/Subscription/Validator/Constraint/ListExistsValidator.php new file mode 100644 index 0000000..68f1b37 --- /dev/null +++ b/src/Subscription/Validator/Constraint/ListExistsValidator.php @@ -0,0 +1,43 @@ +subscriberListRepository = $subscriberListRepository; + } + + public function validate($value, Constraint $constraint): void + { + if (!$constraint instanceof ListExists) { + throw new UnexpectedTypeException($constraint, ListExists::class); + } + + if (null === $value || '' === $value) { + return; + } + + if (!is_string($value)) { + throw new UnexpectedValueException($value, 'string'); + } + + $existingList = $this->subscriberListRepository->find($value); + + if (!$existingList) { + throw new NotFoundHttpException('Subscriber list does not exists.'); + } + } +} From d7b94640e7070b34bf3ff4a6a52d42494a4b9939 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Sat, 24 May 2025 16:25:30 +0400 Subject: [PATCH 17/21] ISSUE-345: use post for export add tests --- .../Controller/SubscriberExportController.php | 51 ++------ .../OpenApi/SwaggerSchemasRequest.php | 49 ++++++++ .../Request/SubscribersExportRequest.php | 5 +- .../SubscriberExportControllerTest.php | 109 ++++++++++++++++++ ...SubscribersExportRequestNormalizerTest.php | 79 +++++++++++++ .../Constraint/ListExistsValidatorTest.php | 86 ++++++++++++++ 6 files changed, 333 insertions(+), 46 deletions(-) create mode 100644 tests/Integration/Subscription/Controller/SubscriberExportControllerTest.php create mode 100644 tests/Unit/Subscription/Serializer/SubscribersExportRequestNormalizerTest.php create mode 100644 tests/Unit/Subscription/Validator/Constraint/ListExistsValidatorTest.php diff --git a/src/Subscription/Controller/SubscriberExportController.php b/src/Subscription/Controller/SubscriberExportController.php index b679256..db3f3ae 100644 --- a/src/Subscription/Controller/SubscriberExportController.php +++ b/src/Subscription/Controller/SubscriberExportController.php @@ -28,11 +28,16 @@ public function __construct( $this->exportManager = $exportManager; } - #[Route('/export', name: 'csv', methods: ['GET'])] - #[OA\Get( + #[Route('/export', name: 'csv', methods: ['POST'])] + #[OA\Post( path: '/subscribers/export', description: 'Export subscribers to CSV file.', summary: 'Export subscribers', + requestBody: new OA\RequestBody( + description: 'Filter parameters for subscribers to export. ', + required: true, + content: new OA\JsonContent(ref: '#/components/schemas/ExportSubscriberRequest') + ), tags: ['subscribers'], parameters: [ new OA\Parameter( @@ -42,48 +47,6 @@ public function __construct( required: true, schema: new OA\Schema(type: 'string') ), - new OA\Parameter( - name: 'date_type', - description: 'What date needs to be used for filtering (any, signup, changed, changelog, subscribed)', - in: 'query', - required: false, - schema: new OA\Schema( - type: 'string', - default: 'any', - enum: ['any', 'signup', 'changed', 'changelog', 'subscribed'] - ) - ), - new OA\Parameter( - name: 'list_id', - description: 'List ID from where to export', - in: 'query', - required: false, - schema: new OA\Schema(type: 'integer') - ), - new OA\Parameter( - name: 'date_from', - description: 'Start date for filtering (format: Y-m-d)', - in: 'query', - required: false, - schema: new OA\Schema(type: 'string', format: 'date') - ), - new OA\Parameter( - name: 'date_to', - description: 'End date for filtering (format: Y-m-d)', - in: 'query', - required: false, - schema: new OA\Schema(type: 'string', format: 'date') - ), - new OA\Parameter( - name: 'columns', - description: 'Columns to include in the export (comma-separated)', - in: 'query', - required: false, - schema: new OA\Schema( - type: 'string', - default: 'id,email,confirmed,blacklisted,bounceCount,createdAt,updatedAt,uniqueId,htmlEmail,disabled,extraData' - ) - ) ], responses: [ new OA\Response( diff --git a/src/Subscription/OpenApi/SwaggerSchemasRequest.php b/src/Subscription/OpenApi/SwaggerSchemasRequest.php index 5e7822b..ac7b12b 100644 --- a/src/Subscription/OpenApi/SwaggerSchemasRequest.php +++ b/src/Subscription/OpenApi/SwaggerSchemasRequest.php @@ -53,6 +53,55 @@ ], type: 'object' )] +#[OA\Schema( + schema: 'ExportSubscriberRequest', + properties: [ + new OA\Property( + property: 'date_type', + description: 'What date needs to be used for filtering (any, signup, changed, changelog, subscribed)', + default: 'any', + enum: ['any', 'signup', 'changed', 'changelog', 'subscribed'] + ), + new OA\Property( + property: 'list_id', + description: 'List ID from where to export', + type: 'integer' + ), + new OA\Property( + property: 'date_from', + description: 'Start date for filtering (format: Y-m-d)', + type: 'string', + format: 'date' + ), + new OA\Property( + property: 'date_to', + description: 'End date for filtering (format: Y-m-d)', + type: 'string', + format: 'date' + ), + new OA\Property( + property: 'columns', + description: 'Columns to include in the export', + type: 'array', + items: new OA\Items(type: 'string'), + default: [ + 'id', + 'email', + 'confirmed', + 'blacklisted', + 'bounceCount', + 'createdAt', + 'updatedAt', + 'uniqueId', + 'htmlEmail', + 'disabled', + 'extraData', + ], + ), + ], + type: 'object' +)] + class SwaggerSchemasRequest { } diff --git a/src/Subscription/Request/SubscribersExportRequest.php b/src/Subscription/Request/SubscribersExportRequest.php index fa05c73..aad7464 100644 --- a/src/Subscription/Request/SubscribersExportRequest.php +++ b/src/Subscription/Request/SubscribersExportRequest.php @@ -52,14 +52,15 @@ class SubscribersExportRequest implements RequestInterface private function resolveDates(): array { - $dateFrom = new DateTimeImmutable($this->dateFrom); - $dateTo = new DateTimeImmutable($this->dateTo); + $dateFrom = $this->dateFrom ? new DateTimeImmutable($this->dateFrom) : null; + $dateTo = $this->dateTo ? new DateTimeImmutable($this->dateTo) : null; return match ($this->dateType) { 'subscribed' => [$dateFrom, $dateTo, null, null, null, null], 'signup' => [null, null, $dateFrom, $dateTo, null, null], 'changed' => [null, null, null, null, $dateFrom, $dateTo], 'any', 'changelog' => [null, null, null, null, null, null], + default => [null, null, null, null, null, null], }; } diff --git a/tests/Integration/Subscription/Controller/SubscriberExportControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberExportControllerTest.php new file mode 100644 index 0000000..39ad2e6 --- /dev/null +++ b/tests/Integration/Subscription/Controller/SubscriberExportControllerTest.php @@ -0,0 +1,109 @@ +get(SubscriberExportController::class) + ); + } + + public function testExportSubscribersWithoutSessionKeyReturnsForbiddenStatus(): void + { + $this->jsonRequest('POST', '/api/v2/subscribers/export'); + + $this->assertHttpForbidden(); + } + + public function testExportSubscribersWithInvalidRequestReturnsUnprocessableEntityStatus(): void + { + $this->authenticatedJsonRequest( + 'POST', + '/api/v2/subscribers/export', + [], + [], + [], + json_encode(['dateType' => 'invalid_type']) + ); + + $this->assertHttpUnprocessableEntity(); + } + + public function testExportSubscribersWithValidRequest(): void + { + $this->authenticatedJsonRequest( + 'POST', + '/api/v2/subscribers/export', + [], + [], + [], + json_encode([ + 'dateType' => 'any', + 'columns' => ['email', 'confirmed', 'blacklisted'] + ]) + ); + + $response = self::getClient()->getResponse(); + self::assertSame(Response::HTTP_OK, $response->getStatusCode()); + self::assertStringContainsString('text/csv', $response->headers->get('Content-Type')); + self::assertStringContainsString( + 'attachment; filename=subscribers_export_', + $response->headers->get('Content-Disposition') + ); + } + + public function testExportSubscribersWithoutListIdFilter(): void + { + $this->authenticatedJsonRequest( + 'POST', + '/api/v2/subscribers/export', + [], + [], + [], + json_encode([ + 'dateType' => 'any', + 'columns' => ['email', 'confirmed', 'blacklisted'] + ]) + ); + + $response = self::getClient()->getResponse(); + self::assertSame(Response::HTTP_OK, $response->getStatusCode()); + self::assertStringContainsString('text/csv', $response->headers->get('Content-Type')); + } + + public function testExportSubscribersWithSpecificColumns(): void + { + $this->authenticatedJsonRequest( + 'POST', + '/api/v2/subscribers/export', + [], + [], + [], + json_encode([ + 'dateType' => 'any', + 'columns' => ['email', 'confirmed'] + ]) + ); + + $response = self::getClient()->getResponse(); + self::assertSame(Response::HTTP_OK, $response->getStatusCode()); + self::assertStringContainsString('text/csv', $response->headers->get('Content-Type')); + } + + public function testGetMethodIsNotAllowed(): void + { + $this->authenticatedJsonRequest('GET', '/api/v2/subscribers/export'); + + $this->assertHttpMethodNotAllowed(); + } +} diff --git a/tests/Unit/Subscription/Serializer/SubscribersExportRequestNormalizerTest.php b/tests/Unit/Subscription/Serializer/SubscribersExportRequestNormalizerTest.php new file mode 100644 index 0000000..b7198c9 --- /dev/null +++ b/tests/Unit/Subscription/Serializer/SubscribersExportRequestNormalizerTest.php @@ -0,0 +1,79 @@ +createMock(SubscribersExportRequest::class); + + $this->assertTrue($normalizer->supportsNormalization($request)); + $this->assertFalse($normalizer->supportsNormalization(new stdClass())); + } + + public function testNormalize(): void + { + $request = new SubscribersExportRequest(); + $request->dateType = 'signup'; + $request->listId = 123; + $request->dateFrom = '2023-01-01'; + $request->dateTo = '2023-12-31'; + $request->columns = ['id', 'email', 'confirmed']; + + $normalizer = new SubscribersExportRequestNormalizer(); + + $expected = [ + 'date_type' => 'signup', + 'list_id' => 123, + 'date_from' => '2023-01-01', + 'date_to' => '2023-12-31', + 'columns' => ['id', 'email', 'confirmed'], + ]; + + $this->assertSame($expected, $normalizer->normalize($request)); + } + + public function testNormalizeWithDefaultValues(): void + { + $request = new SubscribersExportRequest(); + + $normalizer = new SubscribersExportRequestNormalizer(); + + $expected = [ + 'date_type' => 'any', + 'list_id' => null, + 'date_from' => null, + 'date_to' => null, + 'columns' => [ + 'id', + 'email', + 'confirmed', + 'blacklisted', + 'bounceCount', + 'createdAt', + 'updatedAt', + 'uniqueId', + 'htmlEmail', + 'disabled', + 'extraData' + ], + ]; + + $this->assertSame($expected, $normalizer->normalize($request)); + } + + public function testNormalizeWithInvalidObject(): void + { + $normalizer = new SubscribersExportRequestNormalizer(); + $this->assertSame([], $normalizer->normalize(new stdClass())); + } +} diff --git a/tests/Unit/Subscription/Validator/Constraint/ListExistsValidatorTest.php b/tests/Unit/Subscription/Validator/Constraint/ListExistsValidatorTest.php new file mode 100644 index 0000000..fe8c512 --- /dev/null +++ b/tests/Unit/Subscription/Validator/Constraint/ListExistsValidatorTest.php @@ -0,0 +1,86 @@ +subscriberListRepository = $this->createMock(SubscriberListRepository::class); + $context = $this->createMock(ExecutionContextInterface::class); + + $this->validator = new ListExistsValidator($this->subscriberListRepository); + $this->validator->initialize($context); + } + + public function testValidateSkipsNull(): void + { + $this->subscriberListRepository->expects($this->never())->method('find'); + $this->validator->validate(null, new ListExists()); + $this->assertTrue(true); + } + + public function testValidateSkipsEmptyString(): void + { + $this->subscriberListRepository->expects($this->never())->method('find'); + $this->validator->validate('', new ListExists()); + $this->assertTrue(true); + } + + public function testValidateThrowsUnexpectedTypeException(): void + { + $this->expectException(UnexpectedTypeException::class); + $this->validator->validate('123', $this->createMock(Constraint::class)); + } + + public function testValidateThrowsUnexpectedValueException(): void + { + $this->expectException(UnexpectedValueException::class); + $this->validator->validate(123, new ListExists()); + } + + public function testValidateThrowsNotFoundExceptionIfListDoesNotExist(): void + { + $this->subscriberListRepository + ->expects($this->once()) + ->method('find') + ->with('123') + ->willReturn(null); + + $this->expectException(NotFoundHttpException::class); + $this->expectExceptionMessage('Subscriber list does not exists.'); + + $this->validator->validate('123', new ListExists()); + } + + public function testValidatePassesIfListExists(): void + { + $subscriberList = $this->createMock(SubscriberList::class); + + $this->subscriberListRepository + ->expects($this->once()) + ->method('find') + ->with('123') + ->willReturn($subscriberList); + + $this->validator->validate('123', new ListExists()); + $this->assertTrue(true); + } +} From af1eeb006cb04fe4800e63592b1b32a3317a84d3 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Mon, 26 May 2025 22:04:56 +0400 Subject: [PATCH 18/21] ISSUE-345: mark routes --- .../Controller/AdminAttributeDefinitionController.php | 10 +++++----- .../Controller/AdminAttributeValueController.php | 8 ++++---- src/Identity/Controller/AdministratorController.php | 10 +++++----- src/Identity/Controller/SessionController.php | 4 ++-- src/Messaging/Controller/CampaignController.php | 10 +++++----- src/Messaging/Controller/TemplateController.php | 8 ++++---- src/Subscription/Controller/ListMembersController.php | 4 ++-- .../SubscriberAttributeDefinitionController.php | 10 +++++----- .../Controller/SubscriberAttributeValueController.php | 8 ++++---- src/Subscription/Controller/SubscriberController.php | 8 ++++---- .../Controller/SubscriberExportController.php | 2 +- .../Controller/SubscriberImportController.php | 2 +- .../Controller/SubscriberListController.php | 8 ++++---- src/Subscription/Controller/SubscriptionController.php | 4 ++-- 14 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/Identity/Controller/AdminAttributeDefinitionController.php b/src/Identity/Controller/AdminAttributeDefinitionController.php index 3277b89..af65ba5 100644 --- a/src/Identity/Controller/AdminAttributeDefinitionController.php +++ b/src/Identity/Controller/AdminAttributeDefinitionController.php @@ -43,7 +43,7 @@ public function __construct( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/administrators/attributes', - description: 'Returns created admin attribute definition.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created admin attribute definition.', summary: 'Create an admin attribute definition.', requestBody: new OA\RequestBody( description: 'Pass parameters to create admin attribute.', @@ -96,7 +96,7 @@ public function create(Request $request): JsonResponse #[Route('/{definitionId}', name: 'update', requirements: ['definitionId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/administrators/attributes/{definitionId}', - description: 'Returns updated admin attribute definition.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns updated admin attribute definition.', summary: 'Update an admin attribute definition.', requestBody: new OA\RequestBody( description: 'Pass parameters to update admin attribute.', @@ -162,7 +162,7 @@ public function update( #[Route('/{definitionId}', name: 'delete', requirements: ['definitionId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/administrators/attributes/{definitionId}', - description: 'Deletes a single admin attribute definition.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single admin attribute definition.', summary: 'Deletes an attribute definition.', tags: ['admin-attributes'], parameters: [ @@ -215,7 +215,7 @@ public function delete( #[Route('', name: 'get_lists', methods: ['GET'])] #[OA\Get( path: '/administrators/attributes', - description: 'Returns a JSON list of all admin attribute definitions.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all admin attribute definitions.', summary: 'Gets a list of all admin attribute definitions.', tags: ['admin-attributes'], parameters: [ @@ -283,7 +283,7 @@ public function getPaginated(Request $request): JsonResponse #[Route('/{definitionId}', name: 'get_one', requirements: ['definitionId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/administrators/attributes/{definitionId}', - description: 'Returns a single attribute with specified ID.', + 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.', tags: ['admin-attributes'], parameters: [ diff --git a/src/Identity/Controller/AdminAttributeValueController.php b/src/Identity/Controller/AdminAttributeValueController.php index d410fce..21bc5ed 100644 --- a/src/Identity/Controller/AdminAttributeValueController.php +++ b/src/Identity/Controller/AdminAttributeValueController.php @@ -49,7 +49,7 @@ public function __construct( )] #[OA\Post( path: '/administrators/attribute-values/{adminId}/{definitionId}', - description: 'Returns created/updated admin attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created/updated admin attribute.', summary: 'Create/update an admin attribute.', requestBody: new OA\RequestBody( description: 'Pass parameters to create admin attribute.', @@ -134,7 +134,7 @@ public function createOrUpdate( )] #[OA\Delete( path: '/administrators/attribute-values/{adminId}/{definitionId}', - description: 'Deletes a single admin attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single admin attribute.', summary: 'Deletes an attribute.', tags: ['admin-attributes'], parameters: [ @@ -198,7 +198,7 @@ public function delete( #[Route('/{adminId}', name: 'get__list', requirements: ['adminId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/administrators/attribute-values/{adminId}', - description: 'Returns a JSON list of all admin attributes.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all admin attributes.', summary: 'Gets a list of all admin attributes.', tags: ['admin-attributes'], parameters: [ @@ -279,7 +279,7 @@ public function getPaginated( #[Route('/{adminId}/{definitionId}', name: 'get_one', methods: ['GET'])] #[OA\Get( path: '/administrators/attribute-values/{adminId}/{definitionId}', - description: 'Returns a single attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a single attribute.', summary: 'Gets admin attribute.', tags: ['admin-attributes'], parameters: [ diff --git a/src/Identity/Controller/AdministratorController.php b/src/Identity/Controller/AdministratorController.php index 8adaad8..5619f53 100644 --- a/src/Identity/Controller/AdministratorController.php +++ b/src/Identity/Controller/AdministratorController.php @@ -46,7 +46,7 @@ public function __construct( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/administrators', - description: 'Get list of administrators.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Get list of administrators.', summary: 'Get Administrators', tags: ['administrators'], parameters: [ @@ -109,7 +109,7 @@ public function getAdministrators(Request $request): JsonResponse #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/administrators', - description: 'Create a new administrator.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Create a new administrator.', summary: 'Create Administrator', requestBody: new OA\RequestBody( description: 'Administrator data', @@ -148,7 +148,7 @@ public function createAdministrator( #[Route('/{administratorId}', name: 'get_one', requirements: ['administratorId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/administrators/{administratorId}', - description: 'Get administrator by ID.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Get administrator by ID.', summary: 'Get Administrator', tags: ['administrators'], parameters: [ @@ -189,7 +189,7 @@ public function getAdministrator( #[Route('/{administratorId}', name: 'update', requirements: ['administratorId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/administrators/{administratorId}', - description: 'Update an administrator.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Update an administrator.', summary: 'Update Administrator', requestBody: new OA\RequestBody( description: 'Administrator update data', @@ -236,7 +236,7 @@ public function updateAdministrator( #[Route('/{administratorId}', name: 'delete', requirements: ['administratorId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/administrators/{administratorId}', - description: 'Delete an administrator.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Delete an administrator.', summary: 'Delete Administrator', tags: ['administrators'], parameters: [ diff --git a/src/Identity/Controller/SessionController.php b/src/Identity/Controller/SessionController.php index 5ea1c9b..1abad6b 100644 --- a/src/Identity/Controller/SessionController.php +++ b/src/Identity/Controller/SessionController.php @@ -43,7 +43,7 @@ public function __construct( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/sessions', - description: 'Given valid login data, this will generate a login token that will be valid for 1 hour.', + description: '✅ **Status: Stable** – This method is stable and safe for production use. Given valid login data, this will generate a login token that will be valid for 1 hour.', summary: 'Log in or create new session.', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -111,7 +111,7 @@ public function createSession( #[Route('/{sessionId}', name: 'delete', requirements: ['sessionId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/sessions/{sessionId}', - description: 'Delete the session passed as a parameter.', + description: '✅ **Status: Stable** – This method is stable and safe for production use. Delete the session passed as a parameter.', summary: 'Delete a session.', tags: ['sessions'], parameters: [ diff --git a/src/Messaging/Controller/CampaignController.php b/src/Messaging/Controller/CampaignController.php index 3f254e6..17122ee 100644 --- a/src/Messaging/Controller/CampaignController.php +++ b/src/Messaging/Controller/CampaignController.php @@ -49,7 +49,7 @@ public function __construct( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/campaigns', - description: 'Returns a JSON list of all campaigns/messages.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all campaigns/messages.', summary: 'Gets a list of all campaigns.', tags: ['campaigns'], parameters: [ @@ -115,7 +115,7 @@ public function getMessages(Request $request): JsonResponse #[Route('/{messageId}', name: 'get_one', requirements: ['messageId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/campaigns/{messageId}', - description: 'Returns campaign/message by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns campaign/message by id.', summary: 'Gets a campaign by id.', tags: ['campaigns'], parameters: [ @@ -165,7 +165,7 @@ public function getMessage( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/campaigns', - description: 'Returns created message.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created message.', summary: 'Create a message for campaign.', requestBody: new OA\RequestBody( description: 'Create a new message.', @@ -227,7 +227,7 @@ public function createMessage(Request $request, MessageNormalizer $normalizer): #[Route('/{messageId}', name: 'update', requirements: ['messageId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/campaigns/{messageId}', - description: 'Updates campaign/message by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Updates campaign/message by id.', summary: 'Update campaign by id.', requestBody: new OA\RequestBody( description: 'Update message.', @@ -300,7 +300,7 @@ public function updateMessage( #[Route('/{messageId}', name: 'delete', requirements: ['messageId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/campaigns/{messageId}', - description: 'Delete campaign/message by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Delete campaign/message by id.', summary: 'Delete campaign by id.', tags: ['campaigns'], parameters: [ diff --git a/src/Messaging/Controller/TemplateController.php b/src/Messaging/Controller/TemplateController.php index ea73d93..2dfa632 100644 --- a/src/Messaging/Controller/TemplateController.php +++ b/src/Messaging/Controller/TemplateController.php @@ -47,7 +47,7 @@ public function __construct( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/templates', - description: 'Returns a JSON list of all templates.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all templates.', summary: 'Gets a list of all templates.', tags: ['templates'], parameters: [ @@ -115,7 +115,7 @@ public function getTemplates(Request $request): JsonResponse #[Route('/{templateId}', name: 'get_one', requirements: ['templateId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/templates/{templateId}', - description: 'Returns template by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns template by id.', summary: 'Gets a templateI by id.', tags: ['templates'], parameters: [ @@ -170,7 +170,7 @@ public function getTemplate( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/templates', - description: 'Returns a JSON response of created template.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON response of created template.', summary: 'Create a new template.', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -273,7 +273,7 @@ public function createTemplates(Request $request): JsonResponse #[Route('/{templateId}', name: 'delete', requirements: ['templateId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/templates/{templateId}', - description: 'Deletes template by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes template by id.', summary: 'Deletes a template.', tags: ['templates'], parameters: [ diff --git a/src/Subscription/Controller/ListMembersController.php b/src/Subscription/Controller/ListMembersController.php index 86e278a..23929d2 100644 --- a/src/Subscription/Controller/ListMembersController.php +++ b/src/Subscription/Controller/ListMembersController.php @@ -39,7 +39,7 @@ public function __construct( #[Route('/{listId}/subscribers', name: 'get_list', requirements: ['listId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/lists/{listId}/subscribers', - description: 'Returns a JSON list of all subscribers for a subscriber list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all subscribers for a subscriber list.', summary: 'Gets a list of all subscribers of a subscriber list.', tags: ['subscriptions'], parameters: [ @@ -124,7 +124,7 @@ public function getListMembers( #[Route('/{listId}/subscribers/count', name: 'get_count', requirements: ['listId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/lists/{listId}/count', - description: 'Returns a count of all subscribers in a given list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a count of all subscribers in a given list.', summary: 'Gets the total number of subscribers of a list', tags: ['subscriptions'], parameters: [ diff --git a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php index c7db4e7..c7f0010 100644 --- a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php +++ b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php @@ -43,7 +43,7 @@ public function __construct( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/subscriber/attributes', - description: 'Returns created subscriber attribute definition.', + 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.', @@ -96,7 +96,7 @@ public function create(Request $request): JsonResponse #[Route('/{definitionId}', name: 'update', requirements: ['definitionId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/subscriber/attributes/{definitionId}', - description: 'Returns updated subscriber attribute definition.', + 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.', @@ -162,7 +162,7 @@ public function update( #[Route('/{definitionId}', name: 'delete', requirements: ['definitionId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/subscriber/attributes/{definitionId}', - description: 'Deletes a single subscriber attribute definition.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single subscriber attribute definition.', summary: 'Deletes an attribute definition.', tags: ['subscriber-attributes'], parameters: [ @@ -215,7 +215,7 @@ public function delete( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/subscriber/attributes', - description: 'Returns a JSON list of all subscriber attribute definitions.', + 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.', tags: ['subscriber-attributes'], parameters: [ @@ -283,7 +283,7 @@ public function getPaginated(Request $request): JsonResponse #[Route('/{definitionId}', name: 'get_one', requirements: ['definitionId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/subscriber/attributes/{definitionId}', - description: 'Returns a single attribute with specified ID.', + 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.', tags: ['subscriber-attributes'], parameters: [ diff --git a/src/Subscription/Controller/SubscriberAttributeValueController.php b/src/Subscription/Controller/SubscriberAttributeValueController.php index a1b05a8..ea3eea6 100644 --- a/src/Subscription/Controller/SubscriberAttributeValueController.php +++ b/src/Subscription/Controller/SubscriberAttributeValueController.php @@ -49,7 +49,7 @@ public function __construct( )] #[OA\Post( path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', - description: 'Returns created/updated subscriber attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created/updated subscriber attribute.', summary: 'Create/update a subscriber attribute.', requestBody: new OA\RequestBody( description: 'Pass parameters to create subscriber attribute.', @@ -134,7 +134,7 @@ public function createOrUpdate( )] #[OA\Delete( path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', - description: 'Deletes a single subscriber attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single subscriber attribute.', summary: 'Deletes an attribute.', tags: ['subscriber-attributes'], parameters: [ @@ -198,7 +198,7 @@ public function delete( #[Route('/{subscriberId}', name: 'get_list', requirements: ['subscriberId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/subscribers/attribute-values/{subscriberId}', - description: 'Returns a JSON list of all subscriber attributes.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all subscriber attributes.', summary: 'Gets a list of all subscriber attributes.', tags: ['subscriber-attributes'], parameters: [ @@ -281,7 +281,7 @@ public function getPaginated( )] #[OA\Get( path: '/subscribers/attribute-values/{subscriberId}/{definitionId}', - description: 'Returns a single attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a single attribute.', summary: 'Gets subscriber attribute.', tags: ['subscriber-attributes'], parameters: [ diff --git a/src/Subscription/Controller/SubscriberController.php b/src/Subscription/Controller/SubscriberController.php index 692af09..f3343aa 100644 --- a/src/Subscription/Controller/SubscriberController.php +++ b/src/Subscription/Controller/SubscriberController.php @@ -46,7 +46,7 @@ public function __construct( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/subscribers', - description: 'Creates a new subscriber (if there is no subscriber with the given email address yet).', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Creates a new subscriber (if there is no subscriber with the given email address yet).', summary: 'Create a subscriber', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -103,7 +103,7 @@ public function createSubscriber(Request $request): JsonResponse #[Route('/{subscriberId}', name: 'update', requirements: ['subscriberId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/subscribers/{subscriberId}', - description: 'Update subscriber data by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Update subscriber data by id.', summary: 'Update subscriber', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -169,7 +169,7 @@ public function updateSubscriber( #[Route('/{subscriberId}', name: 'get_one', requirements: ['subscriberId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/subscribers/{subscriberId}', - description: 'Get subscriber data by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Get subscriber data by id.', summary: 'Get a subscriber', tags: ['subscribers'], parameters: [ @@ -218,7 +218,7 @@ public function getSubscriber(Request $request, int $subscriberId): JsonResponse #[Route('/{subscriberId}', name: 'delete', requirements: ['subscriberId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/subscribers/{subscriberId}', - description: 'Delete subscriber by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Delete subscriber by id.', summary: 'Delete subscriber', tags: ['subscribers'], parameters: [ diff --git a/src/Subscription/Controller/SubscriberExportController.php b/src/Subscription/Controller/SubscriberExportController.php index db3f3ae..fea0fd9 100644 --- a/src/Subscription/Controller/SubscriberExportController.php +++ b/src/Subscription/Controller/SubscriberExportController.php @@ -31,7 +31,7 @@ public function __construct( #[Route('/export', name: 'csv', methods: ['POST'])] #[OA\Post( path: '/subscribers/export', - description: 'Export subscribers to CSV file.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Export subscribers to CSV file.', summary: 'Export subscribers', requestBody: new OA\RequestBody( description: 'Filter parameters for subscribers to export. ', diff --git a/src/Subscription/Controller/SubscriberImportController.php b/src/Subscription/Controller/SubscriberImportController.php index 7bf31f6..8987781 100644 --- a/src/Subscription/Controller/SubscriberImportController.php +++ b/src/Subscription/Controller/SubscriberImportController.php @@ -34,7 +34,7 @@ public function __construct( #[Route('/import', name: 'csv', methods: ['POST'])] #[OA\Post( path: '/subscribers/import', - description: 'Import subscribers from CSV file.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Import subscribers from CSV file.', summary: 'Import subscribers', requestBody: new OA\RequestBody( required: true, diff --git a/src/Subscription/Controller/SubscriberListController.php b/src/Subscription/Controller/SubscriberListController.php index 5215697..ced7729 100644 --- a/src/Subscription/Controller/SubscriberListController.php +++ b/src/Subscription/Controller/SubscriberListController.php @@ -49,7 +49,7 @@ public function __construct( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/lists', - description: 'Returns a JSON list of all subscriber lists.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all subscriber lists.', summary: 'Gets a list of all subscriber lists.', tags: ['lists'], parameters: [ @@ -113,7 +113,7 @@ public function getLists(Request $request): JsonResponse #[Route('/{listId}', name: 'get_one', requirements: ['listId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/lists/{listId}', - description: 'Returns a single subscriber list with specified ID.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a single subscriber list with specified ID.', summary: 'Gets a subscriber list.', tags: ['lists'], parameters: [ @@ -175,7 +175,7 @@ public function getList( #[Route('/{listId}', name: 'delete', requirements: ['listId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/lists/{listId}', - description: 'Deletes a single subscriber list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single subscriber list.', summary: 'Deletes a list.', tags: ['lists'], parameters: [ @@ -229,7 +229,7 @@ public function deleteList( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/lists', - description: 'Returns created list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created list.', summary: 'Create a subscriber list.', requestBody: new OA\RequestBody( description: 'Pass parameters to create a new subscriber list.', diff --git a/src/Subscription/Controller/SubscriptionController.php b/src/Subscription/Controller/SubscriptionController.php index fa7061e..91041dd 100644 --- a/src/Subscription/Controller/SubscriptionController.php +++ b/src/Subscription/Controller/SubscriptionController.php @@ -43,7 +43,7 @@ public function __construct( #[Route('/{listId}/subscribers', name: 'create', requirements: ['listId' => '\d+'], methods: ['POST'])] #[OA\Post( path: '/lists/{listId}/subscribers', - description: 'Subscribe subscriber to a list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Subscribe subscriber to a list.', summary: 'Create subscription', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -134,7 +134,7 @@ public function createSubscription( #[Route('/{listId}/subscribers', name: 'delete', requirements: ['listId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/lists/{listId}/subscribers', - description: 'Delete subscription.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Delete subscription.', summary: 'Delete subscription', tags: ['subscriptions'], parameters: [ From 512034680e1f04973b32d92ab7cbbead8828ed11 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Tue, 27 May 2025 07:53:39 +0400 Subject: [PATCH 19/21] ISSUE-345: phpcs fix --- .../AdminAttributeDefinitionController.php | 15 ++++++++++----- .../Controller/AdminAttributeValueController.php | 12 ++++++++---- .../Controller/AdministratorController.php | 15 ++++++++++----- src/Identity/Controller/SessionController.php | 6 ++++-- src/Messaging/Controller/CampaignController.php | 15 ++++++++++----- src/Messaging/Controller/TemplateController.php | 12 ++++++++---- .../Controller/ListMembersController.php | 6 ++++-- .../SubscriberAttributeDefinitionController.php | 15 ++++++++++----- .../SubscriberAttributeValueController.php | 12 ++++++++---- .../Controller/SubscriberController.php | 12 ++++++++---- .../Controller/SubscriberExportController.php | 3 ++- .../Controller/SubscriberImportController.php | 3 ++- .../Controller/SubscriberListController.php | 12 ++++++++---- .../Controller/SubscriptionController.php | 6 ++++-- 14 files changed, 96 insertions(+), 48 deletions(-) diff --git a/src/Identity/Controller/AdminAttributeDefinitionController.php b/src/Identity/Controller/AdminAttributeDefinitionController.php index af65ba5..16dcdd3 100644 --- a/src/Identity/Controller/AdminAttributeDefinitionController.php +++ b/src/Identity/Controller/AdminAttributeDefinitionController.php @@ -43,7 +43,8 @@ public function __construct( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/administrators/attributes', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created admin attribute definition.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns created admin attribute definition.', summary: 'Create an admin attribute definition.', requestBody: new OA\RequestBody( description: 'Pass parameters to create admin attribute.', @@ -96,7 +97,8 @@ public function create(Request $request): JsonResponse #[Route('/{definitionId}', name: 'update', requirements: ['definitionId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/administrators/attributes/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns updated admin attribute definition.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns updated admin attribute definition.', summary: 'Update an admin attribute definition.', requestBody: new OA\RequestBody( description: 'Pass parameters to update admin attribute.', @@ -162,7 +164,8 @@ public function update( #[Route('/{definitionId}', name: 'delete', requirements: ['definitionId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/administrators/attributes/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single admin attribute definition.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Deletes a single admin attribute definition.', summary: 'Deletes an attribute definition.', tags: ['admin-attributes'], parameters: [ @@ -215,7 +218,8 @@ public function delete( #[Route('', name: 'get_lists', methods: ['GET'])] #[OA\Get( path: '/administrators/attributes', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all admin attribute definitions.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a JSON list of all admin attribute definitions.', summary: 'Gets a list of all admin attribute definitions.', tags: ['admin-attributes'], parameters: [ @@ -283,7 +287,8 @@ public function getPaginated(Request $request): JsonResponse #[Route('/{definitionId}', name: 'get_one', requirements: ['definitionId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/administrators/attributes/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a single attribute with specified ID.', + 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.', tags: ['admin-attributes'], parameters: [ diff --git a/src/Identity/Controller/AdminAttributeValueController.php b/src/Identity/Controller/AdminAttributeValueController.php index 21bc5ed..5a2f76d 100644 --- a/src/Identity/Controller/AdminAttributeValueController.php +++ b/src/Identity/Controller/AdminAttributeValueController.php @@ -49,7 +49,8 @@ public function __construct( )] #[OA\Post( path: '/administrators/attribute-values/{adminId}/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created/updated admin attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns created/updated admin attribute.', summary: 'Create/update an admin attribute.', requestBody: new OA\RequestBody( description: 'Pass parameters to create admin attribute.', @@ -134,7 +135,8 @@ public function createOrUpdate( )] #[OA\Delete( path: '/administrators/attribute-values/{adminId}/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single admin attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Deletes a single admin attribute.', summary: 'Deletes an attribute.', tags: ['admin-attributes'], parameters: [ @@ -198,7 +200,8 @@ public function delete( #[Route('/{adminId}', name: 'get__list', requirements: ['adminId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/administrators/attribute-values/{adminId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all admin attributes.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a JSON list of all admin attributes.', summary: 'Gets a list of all admin attributes.', tags: ['admin-attributes'], parameters: [ @@ -279,7 +282,8 @@ public function getPaginated( #[Route('/{adminId}/{definitionId}', name: 'get_one', methods: ['GET'])] #[OA\Get( path: '/administrators/attribute-values/{adminId}/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a single attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a single attribute.', summary: 'Gets admin attribute.', tags: ['admin-attributes'], parameters: [ diff --git a/src/Identity/Controller/AdministratorController.php b/src/Identity/Controller/AdministratorController.php index 5619f53..ff79878 100644 --- a/src/Identity/Controller/AdministratorController.php +++ b/src/Identity/Controller/AdministratorController.php @@ -46,7 +46,8 @@ public function __construct( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/administrators', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Get list of administrators.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Get list of administrators.', summary: 'Get Administrators', tags: ['administrators'], parameters: [ @@ -109,7 +110,8 @@ public function getAdministrators(Request $request): JsonResponse #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/administrators', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Create a new administrator.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Create a new administrator.', summary: 'Create Administrator', requestBody: new OA\RequestBody( description: 'Administrator data', @@ -148,7 +150,8 @@ public function createAdministrator( #[Route('/{administratorId}', name: 'get_one', requirements: ['administratorId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/administrators/{administratorId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Get administrator by ID.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Get administrator by ID.', summary: 'Get Administrator', tags: ['administrators'], parameters: [ @@ -189,7 +192,8 @@ public function getAdministrator( #[Route('/{administratorId}', name: 'update', requirements: ['administratorId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/administrators/{administratorId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Update an administrator.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Update an administrator.', summary: 'Update Administrator', requestBody: new OA\RequestBody( description: 'Administrator update data', @@ -236,7 +240,8 @@ public function updateAdministrator( #[Route('/{administratorId}', name: 'delete', requirements: ['administratorId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/administrators/{administratorId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Delete an administrator.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Delete an administrator.', summary: 'Delete Administrator', tags: ['administrators'], parameters: [ diff --git a/src/Identity/Controller/SessionController.php b/src/Identity/Controller/SessionController.php index 1abad6b..5de6ab7 100644 --- a/src/Identity/Controller/SessionController.php +++ b/src/Identity/Controller/SessionController.php @@ -43,7 +43,8 @@ public function __construct( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/sessions', - description: '✅ **Status: Stable** – This method is stable and safe for production use. Given valid login data, this will generate a login token that will be valid for 1 hour.', + description: '✅ **Status: Stable** – This method is stable and safe for production use. ' . + 'Given valid login data, this will generate a login token that will be valid for 1 hour.', summary: 'Log in or create new session.', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -111,7 +112,8 @@ public function createSession( #[Route('/{sessionId}', name: 'delete', requirements: ['sessionId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/sessions/{sessionId}', - description: '✅ **Status: Stable** – This method is stable and safe for production use. Delete the session passed as a parameter.', + description: '✅ **Status: Stable** – This method is stable and safe for production use. ' . + 'Delete the session passed as a parameter.', summary: 'Delete a session.', tags: ['sessions'], parameters: [ diff --git a/src/Messaging/Controller/CampaignController.php b/src/Messaging/Controller/CampaignController.php index 17122ee..d4e74f9 100644 --- a/src/Messaging/Controller/CampaignController.php +++ b/src/Messaging/Controller/CampaignController.php @@ -49,7 +49,8 @@ public function __construct( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/campaigns', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all campaigns/messages.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a JSON list of all campaigns/messages.', summary: 'Gets a list of all campaigns.', tags: ['campaigns'], parameters: [ @@ -115,7 +116,8 @@ public function getMessages(Request $request): JsonResponse #[Route('/{messageId}', name: 'get_one', requirements: ['messageId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/campaigns/{messageId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns campaign/message by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns campaign/message by id.', summary: 'Gets a campaign by id.', tags: ['campaigns'], parameters: [ @@ -165,7 +167,8 @@ public function getMessage( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/campaigns', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created message.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns created message.', summary: 'Create a message for campaign.', requestBody: new OA\RequestBody( description: 'Create a new message.', @@ -227,7 +230,8 @@ public function createMessage(Request $request, MessageNormalizer $normalizer): #[Route('/{messageId}', name: 'update', requirements: ['messageId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/campaigns/{messageId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Updates campaign/message by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Updates campaign/message by id.', summary: 'Update campaign by id.', requestBody: new OA\RequestBody( description: 'Update message.', @@ -300,7 +304,8 @@ public function updateMessage( #[Route('/{messageId}', name: 'delete', requirements: ['messageId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/campaigns/{messageId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Delete campaign/message by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Delete campaign/message by id.', summary: 'Delete campaign by id.', tags: ['campaigns'], parameters: [ diff --git a/src/Messaging/Controller/TemplateController.php b/src/Messaging/Controller/TemplateController.php index 2dfa632..823b174 100644 --- a/src/Messaging/Controller/TemplateController.php +++ b/src/Messaging/Controller/TemplateController.php @@ -47,7 +47,8 @@ public function __construct( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/templates', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all templates.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a JSON list of all templates.', summary: 'Gets a list of all templates.', tags: ['templates'], parameters: [ @@ -115,7 +116,8 @@ public function getTemplates(Request $request): JsonResponse #[Route('/{templateId}', name: 'get_one', requirements: ['templateId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/templates/{templateId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns template by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns template by id.', summary: 'Gets a templateI by id.', tags: ['templates'], parameters: [ @@ -170,7 +172,8 @@ public function getTemplate( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/templates', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON response of created template.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a JSON response of created template.', summary: 'Create a new template.', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -273,7 +276,8 @@ public function createTemplates(Request $request): JsonResponse #[Route('/{templateId}', name: 'delete', requirements: ['templateId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/templates/{templateId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes template by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Deletes template by id.', summary: 'Deletes a template.', tags: ['templates'], parameters: [ diff --git a/src/Subscription/Controller/ListMembersController.php b/src/Subscription/Controller/ListMembersController.php index 23929d2..e64c7de 100644 --- a/src/Subscription/Controller/ListMembersController.php +++ b/src/Subscription/Controller/ListMembersController.php @@ -39,7 +39,8 @@ public function __construct( #[Route('/{listId}/subscribers', name: 'get_list', requirements: ['listId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/lists/{listId}/subscribers', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all subscribers for a subscriber list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a JSON list of all subscribers for a subscriber list.', summary: 'Gets a list of all subscribers of a subscriber list.', tags: ['subscriptions'], parameters: [ @@ -124,7 +125,8 @@ public function getListMembers( #[Route('/{listId}/subscribers/count', name: 'get_count', requirements: ['listId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/lists/{listId}/count', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a count of all subscribers in a given list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a count of all subscribers in a given list.', summary: 'Gets the total number of subscribers of a list', tags: ['subscriptions'], parameters: [ diff --git a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php index c7f0010..b176982 100644 --- a/src/Subscription/Controller/SubscriberAttributeDefinitionController.php +++ b/src/Subscription/Controller/SubscriberAttributeDefinitionController.php @@ -43,7 +43,8 @@ public function __construct( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/subscriber/attributes', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created subscriber attribute definition.', + 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.', @@ -96,7 +97,8 @@ public function create(Request $request): JsonResponse #[Route('/{definitionId}', name: 'update', requirements: ['definitionId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/subscriber/attributes/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns updated subscriber attribute definition.', + 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.', @@ -162,7 +164,8 @@ public function update( #[Route('/{definitionId}', name: 'delete', requirements: ['definitionId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/subscriber/attributes/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single subscriber attribute definition.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Deletes a single subscriber attribute definition.', summary: 'Deletes an attribute definition.', tags: ['subscriber-attributes'], parameters: [ @@ -215,7 +218,8 @@ public function delete( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/subscriber/attributes', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all subscriber attribute definitions.', + 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.', tags: ['subscriber-attributes'], parameters: [ @@ -283,7 +287,8 @@ public function getPaginated(Request $request): JsonResponse #[Route('/{definitionId}', name: 'get_one', requirements: ['definitionId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/subscriber/attributes/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a single attribute with specified ID.', + 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.', tags: ['subscriber-attributes'], parameters: [ diff --git a/src/Subscription/Controller/SubscriberAttributeValueController.php b/src/Subscription/Controller/SubscriberAttributeValueController.php index ea3eea6..cd7e1e4 100644 --- a/src/Subscription/Controller/SubscriberAttributeValueController.php +++ b/src/Subscription/Controller/SubscriberAttributeValueController.php @@ -49,7 +49,8 @@ public function __construct( )] #[OA\Post( path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created/updated subscriber attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns created/updated subscriber attribute.', summary: 'Create/update a subscriber attribute.', requestBody: new OA\RequestBody( description: 'Pass parameters to create subscriber attribute.', @@ -134,7 +135,8 @@ public function createOrUpdate( )] #[OA\Delete( path: '/subscriber/attribute-values/{subscriberId}/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single subscriber attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Deletes a single subscriber attribute.', summary: 'Deletes an attribute.', tags: ['subscriber-attributes'], parameters: [ @@ -198,7 +200,8 @@ public function delete( #[Route('/{subscriberId}', name: 'get_list', requirements: ['subscriberId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/subscribers/attribute-values/{subscriberId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all subscriber attributes.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a JSON list of all subscriber attributes.', summary: 'Gets a list of all subscriber attributes.', tags: ['subscriber-attributes'], parameters: [ @@ -281,7 +284,8 @@ public function getPaginated( )] #[OA\Get( path: '/subscribers/attribute-values/{subscriberId}/{definitionId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a single attribute.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a single attribute.', summary: 'Gets subscriber attribute.', tags: ['subscriber-attributes'], parameters: [ diff --git a/src/Subscription/Controller/SubscriberController.php b/src/Subscription/Controller/SubscriberController.php index f3343aa..bad2924 100644 --- a/src/Subscription/Controller/SubscriberController.php +++ b/src/Subscription/Controller/SubscriberController.php @@ -46,7 +46,8 @@ public function __construct( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/subscribers', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Creates a new subscriber (if there is no subscriber with the given email address yet).', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Creates a new subscriber (if there is no subscriber with the given email address yet).', summary: 'Create a subscriber', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -103,7 +104,8 @@ public function createSubscriber(Request $request): JsonResponse #[Route('/{subscriberId}', name: 'update', requirements: ['subscriberId' => '\d+'], methods: ['PUT'])] #[OA\Put( path: '/subscribers/{subscriberId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Update subscriber data by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Update subscriber data by id.', summary: 'Update subscriber', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -169,7 +171,8 @@ public function updateSubscriber( #[Route('/{subscriberId}', name: 'get_one', requirements: ['subscriberId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/subscribers/{subscriberId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Get subscriber data by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Get subscriber data by id.', summary: 'Get a subscriber', tags: ['subscribers'], parameters: [ @@ -218,7 +221,8 @@ public function getSubscriber(Request $request, int $subscriberId): JsonResponse #[Route('/{subscriberId}', name: 'delete', requirements: ['subscriberId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/subscribers/{subscriberId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Delete subscriber by id.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Delete subscriber by id.', summary: 'Delete subscriber', tags: ['subscribers'], parameters: [ diff --git a/src/Subscription/Controller/SubscriberExportController.php b/src/Subscription/Controller/SubscriberExportController.php index fea0fd9..4827bd5 100644 --- a/src/Subscription/Controller/SubscriberExportController.php +++ b/src/Subscription/Controller/SubscriberExportController.php @@ -31,7 +31,8 @@ public function __construct( #[Route('/export', name: 'csv', methods: ['POST'])] #[OA\Post( path: '/subscribers/export', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. 🚧 **Status: Beta** – This method is under development. Avoid using in production. Export subscribers to CSV file.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Export subscribers to CSV file.', summary: 'Export subscribers', requestBody: new OA\RequestBody( description: 'Filter parameters for subscribers to export. ', diff --git a/src/Subscription/Controller/SubscriberImportController.php b/src/Subscription/Controller/SubscriberImportController.php index 8987781..dcc60d7 100644 --- a/src/Subscription/Controller/SubscriberImportController.php +++ b/src/Subscription/Controller/SubscriberImportController.php @@ -34,7 +34,8 @@ public function __construct( #[Route('/import', name: 'csv', methods: ['POST'])] #[OA\Post( path: '/subscribers/import', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Import subscribers from CSV file.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Import subscribers from CSV file.', summary: 'Import subscribers', requestBody: new OA\RequestBody( required: true, diff --git a/src/Subscription/Controller/SubscriberListController.php b/src/Subscription/Controller/SubscriberListController.php index ced7729..30e6958 100644 --- a/src/Subscription/Controller/SubscriberListController.php +++ b/src/Subscription/Controller/SubscriberListController.php @@ -49,7 +49,8 @@ public function __construct( #[Route('', name: 'get_list', methods: ['GET'])] #[OA\Get( path: '/lists', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a JSON list of all subscriber lists.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a JSON list of all subscriber lists.', summary: 'Gets a list of all subscriber lists.', tags: ['lists'], parameters: [ @@ -113,7 +114,8 @@ public function getLists(Request $request): JsonResponse #[Route('/{listId}', name: 'get_one', requirements: ['listId' => '\d+'], methods: ['GET'])] #[OA\Get( path: '/lists/{listId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns a single subscriber list with specified ID.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns a single subscriber list with specified ID.', summary: 'Gets a subscriber list.', tags: ['lists'], parameters: [ @@ -175,7 +177,8 @@ public function getList( #[Route('/{listId}', name: 'delete', requirements: ['listId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/lists/{listId}', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Deletes a single subscriber list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Deletes a single subscriber list.', summary: 'Deletes a list.', tags: ['lists'], parameters: [ @@ -229,7 +232,8 @@ public function deleteList( #[Route('', name: 'create', methods: ['POST'])] #[OA\Post( path: '/lists', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Returns created list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Returns created list.', summary: 'Create a subscriber list.', requestBody: new OA\RequestBody( description: 'Pass parameters to create a new subscriber list.', diff --git a/src/Subscription/Controller/SubscriptionController.php b/src/Subscription/Controller/SubscriptionController.php index 91041dd..2db3ac4 100644 --- a/src/Subscription/Controller/SubscriptionController.php +++ b/src/Subscription/Controller/SubscriptionController.php @@ -43,7 +43,8 @@ public function __construct( #[Route('/{listId}/subscribers', name: 'create', requirements: ['listId' => '\d+'], methods: ['POST'])] #[OA\Post( path: '/lists/{listId}/subscribers', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Subscribe subscriber to a list.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Subscribe subscriber to a list.', summary: 'Create subscription', requestBody: new OA\RequestBody( description: 'Pass session credentials', @@ -134,7 +135,8 @@ public function createSubscription( #[Route('/{listId}/subscribers', name: 'delete', requirements: ['listId' => '\d+'], methods: ['DELETE'])] #[OA\Delete( path: '/lists/{listId}/subscribers', - description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. Delete subscription.', + description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . + 'Delete subscription.', summary: 'Delete subscription', tags: ['subscriptions'], parameters: [ From 9aefc00b99cf233a6c4dec6b84f475ba085f0d3f Mon Sep 17 00:00:00 2001 From: Tatevik Date: Tue, 27 May 2025 20:48:02 +0400 Subject: [PATCH 20/21] ISSUE-345: update example --- src/Identity/OpenApi/SwaggerSchemasResponse.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Identity/OpenApi/SwaggerSchemasResponse.php b/src/Identity/OpenApi/SwaggerSchemasResponse.php index b5fe0b7..52b8837 100644 --- a/src/Identity/OpenApi/SwaggerSchemasResponse.php +++ b/src/Identity/OpenApi/SwaggerSchemasResponse.php @@ -27,11 +27,11 @@ 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: 'checkbox'), + new OA\Property(property: 'type', type: 'string', example: 'select'), 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: 'list_attributes'), + new OA\Property(property: 'table_name', type: 'string', example: 'ukcounties'), ], type: 'object' )] From 25b304c291d6b7ad4335a6e41c92149336c3117c Mon Sep 17 00:00:00 2001 From: Tatevik Date: Tue, 27 May 2025 20:55:10 +0400 Subject: [PATCH 21/21] ISSUE-345: new tag --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5f8833c..890aafb 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ }, "require": { "php": "^8.1", - "phplist/core": "dev-ISSUE-345", + "phplist/core": "v5.0.0-alpha7", "friendsofsymfony/rest-bundle": "*", "symfony/test-pack": "^1.0", "symfony/process": "^6.4",