From c22bd44ad8736e16dc164c944bb3b3121adcc944 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 19 Nov 2025 10:45:25 +0400 Subject: [PATCH 1/6] Update: subscriber attribute values routes --- .../SubscriberAttributeValueController.php | 28 +++++++++---------- ...SubscriberAttributeValueControllerTest.php | 9 +++--- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/Subscription/Controller/SubscriberAttributeValueController.php b/src/Subscription/Controller/SubscriberAttributeValueController.php index 433cb74..54ec5b4 100644 --- a/src/Subscription/Controller/SubscriberAttributeValueController.php +++ b/src/Subscription/Controller/SubscriberAttributeValueController.php @@ -22,7 +22,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -#[Route('/subscribers/attribute-values', name: 'subscriber_attribute_value_')] +#[Route('/subscribers', name: 'subscriber_attribute_value_')] class SubscriberAttributeValueController extends BaseController { private SubscriberAttributeManager $attributeManager; @@ -44,16 +44,16 @@ public function __construct( } #[Route( - path: '/{subscriberId}/{definitionId}', + path: '/{subscriberId}/attributes/{definitionId}', name: 'create', requirements: ['subscriberId' => '\d+', 'definitionId' => '\d+'], methods: ['POST', 'PUT'] )] #[OA\Post( - path: '/api/v2/subscribers/attribute-values/{subscriberId}/{definitionId}', + path: '/api/v2/subscribers/{subscriberId}/attributes/{definitionId}', description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . 'Returns created/updated subscriber attribute.', - summary: 'Create/update a subscriber attribute.', + summary: 'Create/update a subscriber attribute value.', requestBody: new OA\RequestBody( description: 'Pass parameters to create subscriber attribute.', required: true, @@ -130,13 +130,13 @@ public function createOrUpdate( } #[Route( - path: '/{subscriberId}/{definitionId}', + path: '/{subscriberId}/attributes/{definitionId}', name: 'delete', requirements: ['subscriberId' => '\d+', 'definitionId' => '\d+'], methods: ['DELETE'] )] #[OA\Delete( - path: '/api/v2/subscribers/attribute-values/{subscriberId}/{definitionId}', + path: '/api/v2/subscribers/{subscriberId}/attributes/{definitionId}', description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . 'Deletes a single subscriber attribute.', summary: 'Deletes an attribute.', @@ -200,9 +200,9 @@ public function delete( return $this->json(null, Response::HTTP_NO_CONTENT); } - #[Route('/{subscriberId}', name: 'get_list', requirements: ['subscriberId' => '\d+'], methods: ['GET'])] + #[Route('/{subscriberId}/attributes', name: 'get_list', requirements: ['subscriberId' => '\d+'], methods: ['GET'])] #[OA\Get( - path: '/api/v2/subscribers/attribute-values/{subscriberId}', + path: '/api/v2/subscribers/{subscriberId}/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.', @@ -270,23 +270,23 @@ public function getPaginated( return $this->json( $this->paginatedDataProvider->getPaginatedList( - $request, - $this->normalizer, - SubscriberAttributeValue::class, - $filter + request: $request, + normalizer: $this->normalizer, + className: SubscriberAttributeValue::class, + filter: $filter ), Response::HTTP_OK ); } #[Route( - path: '/{subscriberId}/{definitionId}', + path: '/{subscriberId}/attributes/{definitionId}', name: 'get_one', requirements: ['subscriberId' => '\d+', 'definitionId' => '\d+'], methods: ['GET'] )] #[OA\Get( - path: '/api/v2/subscribers/attribute-values/{subscriberId}/{definitionId}', + path: '/api/v2/subscribers/{subscriberId}/attributes/{definitionId}', description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' . 'Returns a single attribute.', summary: 'Gets subscriber attribute.', diff --git a/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php b/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php index 4b1a84c..419ea92 100644 --- a/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php +++ b/tests/Integration/Subscription/Controller/SubscriberAttributeValueControllerTest.php @@ -12,7 +12,6 @@ class SubscriberAttributeValueControllerTest extends AbstractTestController { - public function testControllerIsAvailableViaContainer(): void { self::assertInstanceOf( @@ -34,7 +33,7 @@ public function testCreateOrUpdateAttributeValue(): void $this->authenticatedJsonRequest( 'post', - '/api/v2/subscribers/attribute-values/' . $subscriberId . '/' . $definitionId, + '/api/v2/subscribers/' . $subscriberId . '/attributes/' . $definitionId, [], [], [], @@ -55,7 +54,7 @@ public function testDeleteAttributeValue(): void $this->authenticatedJsonRequest( 'delete', - '/api/v2/subscribers/attribute-values/1/1' + '/api/v2/subscribers/1/attributes/1' ); $this->assertHttpNoContent(); @@ -70,7 +69,7 @@ public function testGetPaginatedAttributes(): void $this->authenticatedJsonRequest( 'get', - '/api/v2/subscribers/attribute-values/1' + '/api/v2/subscribers/1/attributes' ); $this->assertHttpOkay(); @@ -83,7 +82,7 @@ public function testAttributeValueNotFoundReturns404(): void { $this->authenticatedJsonRequest( 'get', - '/api/v2/subscribers/attribute-values/999/999' + '/api/v2/subscribers/999/attributes/999' ); $this->assertHttpNotFound(); From d4d0aeb21ecdc597437592ea75bffd47e592121f Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 19 Nov 2025 10:47:29 +0400 Subject: [PATCH 2/6] Update: remove brackets --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index aad3619..0c6c698 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,3 @@ -"@coderabbitai summary" +@coderabbitai summary Thanks for contributing to phpList! From c263e0dbdae506a5ccd8f6ef07e0fe48a24dd1ff Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 19 Nov 2025 10:56:07 +0400 Subject: [PATCH 3/6] Swagger schemas in request/normalizer --- .../OpenApi/SwaggerSchemasRequest.php | 122 ------------- .../OpenApi/SwaggerSchemasResponse.php | 48 ----- .../AdminAttributeDefinitionRequest.php | 23 +++ .../Request/CreateAdministratorRequest.php | 46 +++++ .../Request/UpdateAdministratorRequest.php | 46 ++++- .../AdminAttributeDefinitionNormalizer.php | 13 ++ .../AdminAttributeValueNormalizer.php | 10 + .../Serializer/AdministratorNormalizer.php | 17 ++ .../OpenApi/SwaggerSchemasRequest.php | 82 --------- .../OpenApi/SwaggerSchemasResponse.php | 140 -------------- .../Request/Message/MessageContentRequest.php | 12 ++ .../Request/Message/MessageFormatRequest.php | 21 +++ .../Message/MessageMetadataRequest.php | 8 + .../Request/Message/MessageOptionsRequest.php | 12 ++ .../Message/MessageScheduleRequest.php | 23 +++ .../Serializer/BounceRegexNormalizer.php | 16 ++ .../Serializer/ListMessageNormalizer.php | 21 +++ .../Serializer/MessageNormalizer.php | 76 ++++++++ .../Serializer/TemplateImageNormalizer.php | 22 +++ .../Serializer/TemplateNormalizer.php | 19 ++ src/PhpListRestBundle.php | 51 ++++++ .../OpenApi/SwaggerSchemasRequest.php | 9 - .../OpenApi/SwaggerSchemasResponse.php | 129 ------------- .../CampaignStatisticsNormalizer.php | 17 ++ .../Serializer/TopDomainsNormalizer.php | 20 ++ .../Serializer/TopLocalPartsNormalizer.php | 21 +++ .../ViewOpensStatisticsNormalizer.php | 13 ++ .../OpenApi/SwaggerSchemasResponse.php | 171 ------------------ .../AttributeDefinitionNormalizer.php | 30 ++- .../Serializer/SubscribePageNormalizer.php | 10 + .../SubscriberAttributeValueNormalizer.php | 9 + .../SubscriberHistoryNormalizer.php | 18 ++ .../Serializer/SubscriberListNormalizer.php | 20 ++ .../Serializer/SubscriberNormalizer.php | 26 +++ .../Serializer/SubscriberOnlyNormalizer.php | 21 +++ .../Serializer/SubscriptionNormalizer.php | 15 ++ 36 files changed, 654 insertions(+), 703 deletions(-) delete mode 100644 src/Identity/OpenApi/SwaggerSchemasRequest.php delete mode 100644 src/Identity/OpenApi/SwaggerSchemasResponse.php delete mode 100644 src/Messaging/OpenApi/SwaggerSchemasRequest.php delete mode 100644 src/Messaging/OpenApi/SwaggerSchemasResponse.php delete mode 100644 src/Statistics/OpenApi/SwaggerSchemasRequest.php delete mode 100644 src/Statistics/OpenApi/SwaggerSchemasResponse.php delete mode 100644 src/Subscription/OpenApi/SwaggerSchemasResponse.php diff --git a/src/Identity/OpenApi/SwaggerSchemasRequest.php b/src/Identity/OpenApi/SwaggerSchemasRequest.php deleted file mode 100644 index 3b32ded..0000000 --- a/src/Identity/OpenApi/SwaggerSchemasRequest.php +++ /dev/null @@ -1,122 +0,0 @@ - true, 'campaigns' => false, 'statistics' => true, 'settings' => 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 - ), - new OA\Property( - property: 'privileges', - description: 'Array of privileges where keys are privilege names and values are booleans', - properties: [ - new OA\Property(property: 'subscribers', type: 'boolean', example: true), - new OA\Property(property: 'campaigns', type: 'boolean', example: false), - new OA\Property(property: 'statistics', type: 'boolean', example: true), - new OA\Property(property: 'settings', type: 'boolean', example: false), - ], - type: 'object', - example: ['subscribers' => true, 'campaigns' => false, 'statistics' => true, 'settings' => false] - ), - ], - type: 'object' -)] -#[OA\Schema( - schema: 'AdminAttributeDefinitionRequest', - required: ['name'], - properties: [ - new OA\Property(property: 'name', type: 'string', format: 'string', example: 'Country'), - new OA\Property( - property: 'type', - type: 'string', - enum: [ - AttributeTypeEnum::TextLine, - AttributeTypeEnum::Hidden, - ], - example: 'hidden', - nullable: true - ), - new OA\Property(property: 'order', type: 'number', example: 12), - new OA\Property(property: 'default_value', type: 'string', example: 'United States'), - new OA\Property(property: 'required', type: 'boolean', example: true), - ], - type: 'object' -)] -class SwaggerSchemasRequest -{ -} diff --git a/src/Identity/OpenApi/SwaggerSchemasResponse.php b/src/Identity/OpenApi/SwaggerSchemasResponse.php deleted file mode 100644 index 2d83cff..0000000 --- a/src/Identity/OpenApi/SwaggerSchemasResponse.php +++ /dev/null @@ -1,48 +0,0 @@ - true, 'campaigns' => false, 'statistics' => true, 'settings' => false] + ), + ], + type: 'object' +)] class CreateAdministratorRequest implements RequestInterface { #[Assert\NotBlank] diff --git a/src/Identity/Request/UpdateAdministratorRequest.php b/src/Identity/Request/UpdateAdministratorRequest.php index de9d725..93d7b4d 100644 --- a/src/Identity/Request/UpdateAdministratorRequest.php +++ b/src/Identity/Request/UpdateAdministratorRequest.php @@ -4,14 +4,58 @@ namespace PhpList\RestBundle\Identity\Request; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Identity\Model\Administrator; use PhpList\Core\Domain\Identity\Model\Dto\UpdateAdministratorDto; -use PhpList\Core\Domain\Identity\Model\PrivilegeFlag; use PhpList\RestBundle\Common\Request\RequestInterface; use PhpList\RestBundle\Identity\Validator\Constraint\UniqueEmail; use PhpList\RestBundle\Identity\Validator\Constraint\UniqueLoginName; use Symfony\Component\Validator\Constraints as Assert; +#[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 + ), + new OA\Property( + property: 'privileges', + description: 'Array of privileges where keys are privilege names and values are booleans', + properties: [ + new OA\Property(property: 'subscribers', type: 'boolean', example: true), + new OA\Property(property: 'campaigns', type: 'boolean', example: false), + new OA\Property(property: 'statistics', type: 'boolean', example: true), + new OA\Property(property: 'settings', type: 'boolean', example: false), + ], + type: 'object', + example: ['subscribers' => true, 'campaigns' => false, 'statistics' => true, 'settings' => false] + ), + ], + type: 'object' +)] class UpdateAdministratorRequest implements RequestInterface { public int $administratorId; diff --git a/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php b/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php index 2c13171..a644a73 100644 --- a/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php +++ b/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php @@ -4,9 +4,22 @@ namespace PhpList\RestBundle\Identity\Serializer; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Identity\Model\AdminAttributeDefinition; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +#[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: 'hidden'), + new OA\Property(property: 'list_order', type: 'integer', example: 12), + new OA\Property(property: 'default_value', type: 'string', example: 'United States'), + new OA\Property(property: 'required', type: 'boolean', example: true), + ], + type: 'object' +)] class AdminAttributeDefinitionNormalizer implements NormalizerInterface { /** diff --git a/src/Identity/Serializer/AdminAttributeValueNormalizer.php b/src/Identity/Serializer/AdminAttributeValueNormalizer.php index abdc4eb..d48e74e 100644 --- a/src/Identity/Serializer/AdminAttributeValueNormalizer.php +++ b/src/Identity/Serializer/AdminAttributeValueNormalizer.php @@ -4,9 +4,19 @@ namespace PhpList\RestBundle\Identity\Serializer; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Identity\Model\AdminAttributeValue; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +#[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 AdminAttributeValueNormalizer implements NormalizerInterface { public function __construct( diff --git a/src/Identity/Serializer/AdministratorNormalizer.php b/src/Identity/Serializer/AdministratorNormalizer.php index 5382bf1..fe67b9a 100644 --- a/src/Identity/Serializer/AdministratorNormalizer.php +++ b/src/Identity/Serializer/AdministratorNormalizer.php @@ -4,11 +4,28 @@ namespace PhpList\RestBundle\Identity\Serializer; +use OpenApi\Attributes as OA; use DateTimeInterface; use InvalidArgumentException; use PhpList\Core\Domain\Identity\Model\Administrator; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +#[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' +)] class AdministratorNormalizer implements NormalizerInterface { /** diff --git a/src/Messaging/OpenApi/SwaggerSchemasRequest.php b/src/Messaging/OpenApi/SwaggerSchemasRequest.php deleted file mode 100644 index 8aef402..0000000 --- a/src/Messaging/OpenApi/SwaggerSchemasRequest.php +++ /dev/null @@ -1,82 +0,0 @@ -', - nullable: true - ), - new OA\Property(property: 'to_field', type: 'string', example: '', nullable: true), - new OA\Property(property: 'reply_to', type: 'string', nullable: true), - new OA\Property(property: 'user_selection', type: 'string', nullable: true), - ], - type: 'object' - ), - ], - type: 'object' -)] -#[OA\Schema( - schema: 'BounceRegex', - properties: [ - new OA\Property(property: 'id', type: 'integer', example: 10), - new OA\Property(property: 'regex', type: 'string', example: '/mailbox is full/i'), - new OA\Property(property: 'regex_hash', type: 'string', example: 'd41d8cd98f00b204e9800998ecf8427e'), - new OA\Property(property: 'action', type: 'string', example: 'delete', nullable: true), - new OA\Property(property: 'list_order', type: 'integer', example: 0, nullable: true), - new OA\Property(property: 'admin_id', type: 'integer', example: 1, nullable: true), - new OA\Property(property: 'comment', type: 'string', example: 'Auto-generated rule', nullable: true), - new OA\Property(property: 'status', type: 'string', example: 'active', nullable: true), - new OA\Property(property: 'count', type: 'integer', example: 5, nullable: true), - ], - type: 'object' -)] -class SwaggerSchemasResponse -{ -} diff --git a/src/Messaging/Request/Message/MessageContentRequest.php b/src/Messaging/Request/Message/MessageContentRequest.php index 9363058..a8d759c 100644 --- a/src/Messaging/Request/Message/MessageContentRequest.php +++ b/src/Messaging/Request/Message/MessageContentRequest.php @@ -4,9 +4,21 @@ namespace PhpList\RestBundle\Messaging\Request\Message; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageContentDto; use Symfony\Component\Validator\Constraints as Assert; +#[OA\Schema( + schema: 'MessageContentRequest', + required: ['subject', 'text', 'text_message', 'footer'], + properties: [ + new OA\Property(property: 'subject', type: 'string', example: 'Campaign Subject'), + new OA\Property(property: 'text', type: 'string', example: 'Full text content'), + new OA\Property(property: 'text_message', type: 'string', example: 'Short text message'), + new OA\Property(property: 'footer', type: 'string', example: 'Unsubscribe link here'), + ], + type: 'object' +)] class MessageContentRequest implements RequestDtoInterface { #[Assert\NotBlank] diff --git a/src/Messaging/Request/Message/MessageFormatRequest.php b/src/Messaging/Request/Message/MessageFormatRequest.php index 3dd20d8..4876706 100644 --- a/src/Messaging/Request/Message/MessageFormatRequest.php +++ b/src/Messaging/Request/Message/MessageFormatRequest.php @@ -4,9 +4,30 @@ namespace PhpList\RestBundle\Messaging\Request\Message; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageFormatDto; use Symfony\Component\Validator\Constraints as Assert; +#[OA\Schema( + schema: 'MessageFormatRequest', + required: ['html_formated', 'send_format', 'format_options'], + properties: [ + new OA\Property(property: 'html_formated', type: 'boolean', example: true), + new OA\Property( + property: 'send_format', + type: 'string', + enum: ['html', 'text', 'invite'], + example: 'html' + ), + new OA\Property( + property: 'format_options', + type: 'array', + items: new OA\Items(type: 'string', enum: ['text', 'html', 'pdf']), + example: ['html'] + ), + ], + type: 'object' +)] class MessageFormatRequest implements RequestDtoInterface { #[Assert\Type('bool')] diff --git a/src/Messaging/Request/Message/MessageMetadataRequest.php b/src/Messaging/Request/Message/MessageMetadataRequest.php index 03fd332..7679d84 100644 --- a/src/Messaging/Request/Message/MessageMetadataRequest.php +++ b/src/Messaging/Request/Message/MessageMetadataRequest.php @@ -4,10 +4,18 @@ namespace PhpList\RestBundle\Messaging\Request\Message; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageMetadataDto; use PhpList\Core\Domain\Messaging\Model\Message\MessageStatus; use Symfony\Component\Validator\Constraints as Assert; +#[OA\Schema( + schema: 'MessageMetadataRequest', + properties: [ + new OA\Property(property: 'status', type: 'string', example: 'draft'), + ], + type: 'object' +)] class MessageMetadataRequest implements RequestDtoInterface { #[Assert\NotBlank] diff --git a/src/Messaging/Request/Message/MessageOptionsRequest.php b/src/Messaging/Request/Message/MessageOptionsRequest.php index f2f3485..f7820e7 100644 --- a/src/Messaging/Request/Message/MessageOptionsRequest.php +++ b/src/Messaging/Request/Message/MessageOptionsRequest.php @@ -4,9 +4,21 @@ namespace PhpList\RestBundle\Messaging\Request\Message; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageOptionsDto; use Symfony\Component\Validator\Constraints as Assert; +#[OA\Schema( + schema: 'MessageOptionsRequest', + required: ['from_field'], + properties: [ + new OA\Property(property: 'from_field', type: 'string', example: 'info@example.com'), + new OA\Property(property: 'to_field', type: 'string', example: 'subscriber@example.com'), + new OA\Property(property: 'reply_to', type: 'string', example: 'reply@example.com'), + new OA\Property(property: 'user_selection', type: 'string', example: 'all-active-users'), + ], + type: 'object' +)] class MessageOptionsRequest implements RequestDtoInterface { #[Assert\NotBlank] diff --git a/src/Messaging/Request/Message/MessageScheduleRequest.php b/src/Messaging/Request/Message/MessageScheduleRequest.php index ed6aa8e..09af5ca 100644 --- a/src/Messaging/Request/Message/MessageScheduleRequest.php +++ b/src/Messaging/Request/Message/MessageScheduleRequest.php @@ -4,9 +4,32 @@ namespace PhpList\RestBundle\Messaging\Request\Message; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\Dto\Message\MessageScheduleDto; use Symfony\Component\Validator\Constraints as Assert; +#[OA\Schema( + schema: 'MessageScheduleRequest', + required: ['embargo'], + properties: [ + new OA\Property(property: 'embargo', type: 'string', format: 'date-time', example: '2025-04-17 09:00:00'), + new OA\Property(property: 'repeat_interval', type: 'string', example: '24 hours'), + new OA\Property( + property: 'repeat_until', + type: 'string', + format: 'date-time', + example: '2025-04-30T00:00:00+04:00' + ), + new OA\Property(property: 'requeue_interval', type: 'string', example: '12 hours'), + new OA\Property( + property: 'requeue_until', + type: 'string', + format: 'date-time', + example: '2025-04-20T00:00:00+04:00' + ), + ], + type: 'object' +)] class MessageScheduleRequest implements RequestDtoInterface { public ?int $repeatInterval = null; diff --git a/src/Messaging/Serializer/BounceRegexNormalizer.php b/src/Messaging/Serializer/BounceRegexNormalizer.php index 5771bd8..47d285f 100644 --- a/src/Messaging/Serializer/BounceRegexNormalizer.php +++ b/src/Messaging/Serializer/BounceRegexNormalizer.php @@ -4,9 +4,25 @@ namespace PhpList\RestBundle\Messaging\Serializer; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\BounceRegex; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +#[OA\Schema( + schema: 'BounceRegex', + properties: [ + new OA\Property(property: 'id', type: 'integer', example: 10), + new OA\Property(property: 'regex', type: 'string', example: '/mailbox is full/i'), + new OA\Property(property: 'regex_hash', type: 'string', example: 'd41d8cd98f00b204e9800998ecf8427e'), + new OA\Property(property: 'action', type: 'string', example: 'delete', nullable: true), + new OA\Property(property: 'list_order', type: 'integer', example: 0, nullable: true), + new OA\Property(property: 'admin_id', type: 'integer', example: 1, nullable: true), + new OA\Property(property: 'comment', type: 'string', example: 'Auto-generated rule', nullable: true), + new OA\Property(property: 'status', type: 'string', example: 'active', nullable: true), + new OA\Property(property: 'count', type: 'integer', example: 5, nullable: true), + ], + type: 'object' +)] class BounceRegexNormalizer implements NormalizerInterface { /** diff --git a/src/Messaging/Serializer/ListMessageNormalizer.php b/src/Messaging/Serializer/ListMessageNormalizer.php index 8534827..b3e9da1 100644 --- a/src/Messaging/Serializer/ListMessageNormalizer.php +++ b/src/Messaging/Serializer/ListMessageNormalizer.php @@ -4,10 +4,31 @@ namespace PhpList\RestBundle\Messaging\Serializer; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\ListMessage; use PhpList\RestBundle\Subscription\Serializer\SubscriberListNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +#[OA\Schema( + schema: 'ListMessage', + properties: [ + new OA\Property(property: 'id', type: 'integer', example: 1), + new OA\Property(property: 'message', ref: '#/components/schemas/Message'), + new OA\Property(property: 'subscriber_list', ref: '#/components/schemas/SubscriberList'), + new OA\Property( + property: 'created_at', + type: 'string', + format: 'date-time', + example: '2022-12-01T10:00:00Z' + ), + new OA\Property( + property: 'updated_at', + type: 'string', + format: 'date-time', + example: '2022-12-01T10:00:00Z' + ), + ], +)] class ListMessageNormalizer implements NormalizerInterface { public function __construct( diff --git a/src/Messaging/Serializer/MessageNormalizer.php b/src/Messaging/Serializer/MessageNormalizer.php index dcad635..5c1f3e6 100644 --- a/src/Messaging/Serializer/MessageNormalizer.php +++ b/src/Messaging/Serializer/MessageNormalizer.php @@ -4,9 +4,85 @@ namespace PhpList\RestBundle\Messaging\Serializer; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\Message; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +#[OA\Schema( + schema: 'Message', + properties: [ + new OA\Property(property: 'id', type: 'integer'), + new OA\Property(property: 'unique_id', type: 'string', example: '2df6b147-8470-45ed-8e4e-86aa01af400d'), + new OA\Property( + property: 'template', + ref: '#/components/schemas/Template', + nullable: true + ), + new OA\Property( + property: 'message_content', + properties: [ + new OA\Property(property: 'subject', type: 'string', example: 'Newsletter'), + new OA\Property(property: 'text', type: 'string', example: 'Hello World!'), + new OA\Property(property: 'text_message', type: 'string'), + new OA\Property(property: 'footer', type: 'string', example: 'This is a footer'), + ], + type: 'object' + ), + new OA\Property( + property: 'message_format', + properties: [ + new OA\Property(property: 'html_formated', type: 'boolean'), + new OA\Property(property: 'send_format', type: 'string', example: 'text', nullable: true), + new OA\Property( + property: 'format_options', + type: 'array', + items: new OA\Items(type: 'string'), + example: ['as_html', 'as_text'], + ), + ], + type: 'object' + ), + new OA\Property( + property: 'message_metadata', + properties: [ + new OA\Property(property: 'status', type: 'string', example: 'sent'), + new OA\Property(property: 'processed', type: 'bool', example: true), + new OA\Property(property: 'views', type: 'integer', example: 12), + new OA\Property(property: 'bounce_count', type: 'integer'), + new OA\Property(property: 'entered', type: 'string', format: 'date-time', nullable: true), + new OA\Property(property: 'sent', type: 'string', format: 'date-time', nullable: true), + ], + type: 'object' + ), + new OA\Property( + property: 'message_schedule', + properties: [ + new OA\Property(property: 'repeat_interval', type: 'string', nullable: true), + new OA\Property(property: 'repeat_until', type: 'string', format: 'date-time', nullable: true), + new OA\Property(property: 'requeue_interval', type: 'string', nullable: true), + new OA\Property(property: 'requeue_until', type: 'string', format: 'date-time', nullable: true), + new OA\Property(property: 'embargo', type: 'string', example: '2023-01-01T12:00:00Z', nullable: true), + ], + type: 'object' + ), + new OA\Property( + property: 'message_options', + properties: [ + new OA\Property( + property: 'from_field', + type: 'string', + example: ' My Name ', + nullable: true + ), + new OA\Property(property: 'to_field', type: 'string', example: '', nullable: true), + new OA\Property(property: 'reply_to', type: 'string', nullable: true), + new OA\Property(property: 'user_selection', type: 'string', nullable: true), + ], + type: 'object' + ), + ], + type: 'object' +)] class MessageNormalizer implements NormalizerInterface { public function __construct(private readonly TemplateNormalizer $templateNormalizer) diff --git a/src/Messaging/Serializer/TemplateImageNormalizer.php b/src/Messaging/Serializer/TemplateImageNormalizer.php index 64ced9a..599471f 100644 --- a/src/Messaging/Serializer/TemplateImageNormalizer.php +++ b/src/Messaging/Serializer/TemplateImageNormalizer.php @@ -4,9 +4,31 @@ namespace PhpList\RestBundle\Messaging\Serializer; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\TemplateImage; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +#[OA\Schema( + schema: 'TemplateImage', + properties: [ + new OA\Property(property: 'id', type: 'integer', example: 12), + new OA\Property(property: 'template_id', type: 'integer', example: 1), + new OA\Property(property: 'mimetype', type: 'string', example: 'image/png', nullable: true), + new OA\Property(property: 'filename', type: 'string', example: 'header.png', nullable: true), + new OA\Property( + property: 'data', + description: 'Base64-encoded image data', + type: 'string', + format: 'byte', + example: 'iVBORw0KGgoAAAANSUhEUgAAA...', + nullable: true + ), + new OA\Property(property: 'width', type: 'integer', example: 600, nullable: true), + new OA\Property(property: 'height', type: 'integer', example: 200, nullable: true), + ], + type: 'object', + nullable: true +)] class TemplateImageNormalizer implements NormalizerInterface { /** diff --git a/src/Messaging/Serializer/TemplateNormalizer.php b/src/Messaging/Serializer/TemplateNormalizer.php index 669f7a4..ea047e1 100644 --- a/src/Messaging/Serializer/TemplateNormalizer.php +++ b/src/Messaging/Serializer/TemplateNormalizer.php @@ -4,10 +4,29 @@ namespace PhpList\RestBundle\Messaging\Serializer; +use OpenApi\Attributes as OA; use PhpList\Core\Domain\Messaging\Model\Template; use PhpList\Core\Domain\Messaging\Model\TemplateImage; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +#[OA\Schema( + schema: 'Template', + properties: [ + new OA\Property(property: 'id', type: 'integer', example: 1), + new OA\Property(property: 'title', type: 'string', example: 'Newsletter'), + new OA\Property(property: 'content', type: 'string', example: 'Hello World!', nullable: true), + new OA\Property(property: 'text', type: 'string', nullable: true), + new OA\Property(property: 'order', type: 'integer', nullable: true), + new OA\Property( + property: 'images', + type: 'array', + items: new OA\Items(ref: '#/components/schemas/TemplateImage'), + nullable: true + ), + ], + type: 'object', + nullable: true +)] class TemplateNormalizer implements NormalizerInterface { public function __construct(private readonly TemplateImageNormalizer $templateImageNormalizer) diff --git a/src/PhpListRestBundle.php b/src/PhpListRestBundle.php index 2d27fae..a856c86 100644 --- a/src/PhpListRestBundle.php +++ b/src/PhpListRestBundle.php @@ -11,6 +11,7 @@ * This bundle provides the REST API for phpList. * * @author Oliver Klee + * @author Tatevik Grigoryan */ #[OA\Info( version: '1.0.0', @@ -28,6 +29,56 @@ url: 'https://www.phplist.com/api/v2', description: 'Production server' )] +#[OA\Schema( + schema: 'DetailedDomainStats', + properties: [ + new OA\Property( + property: 'domains', + type: 'array', + items: new OA\Items( + properties: [ + new OA\Property(property: 'domain', type: 'string'), + new OA\Property( + property: 'confirmed', + properties: [ + new OA\Property(property: 'count', type: 'integer'), + new OA\Property(property: 'percentage', type: 'number', format: 'float'), + ], + type: 'object' + ), + new OA\Property( + property: 'unconfirmed', + properties: [ + new OA\Property(property: 'count', type: 'integer'), + new OA\Property(property: 'percentage', type: 'number', format: 'float'), + ], + type: 'object' + ), + new OA\Property( + property: 'blacklisted', + properties: [ + new OA\Property(property: 'count', type: 'integer'), + new OA\Property(property: 'percentage', type: 'number', format: 'float'), + ], + type: 'object' + ), + new OA\Property( + property: 'total', + properties: [ + new OA\Property(property: 'count', type: 'integer'), + new OA\Property(property: 'percentage', type: 'number', format: 'float'), + ], + type: 'object' + ), + ], + type: 'object' + ) + ), + new OA\Property(property: 'total', type: 'integer'), + ], + type: 'object', + nullable: true +)] class PhpListRestBundle extends Bundle { } diff --git a/src/Statistics/OpenApi/SwaggerSchemasRequest.php b/src/Statistics/OpenApi/SwaggerSchemasRequest.php deleted file mode 100644 index 1dcb53a..0000000 --- a/src/Statistics/OpenApi/SwaggerSchemasRequest.php +++ /dev/null @@ -1,9 +0,0 @@ - Date: Wed, 19 Nov 2025 21:46:23 +0400 Subject: [PATCH 4/6] After review 0 --- src/Identity/Request/UpdateAdministratorRequest.php | 4 ++-- .../Serializer/AdminAttributeDefinitionNormalizer.php | 1 - src/Identity/Serializer/AdminAttributeValueNormalizer.php | 2 +- src/Identity/Serializer/AdministratorNormalizer.php | 3 ++- src/Messaging/Request/Message/MessageScheduleRequest.php | 4 ++-- src/Messaging/Serializer/ListMessageNormalizer.php | 1 + src/Subscription/Serializer/AttributeDefinitionNormalizer.php | 3 +-- src/Subscription/Serializer/SubscriberHistoryNormalizer.php | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Identity/Request/UpdateAdministratorRequest.php b/src/Identity/Request/UpdateAdministratorRequest.php index 93d7b4d..f1432d4 100644 --- a/src/Identity/Request/UpdateAdministratorRequest.php +++ b/src/Identity/Request/UpdateAdministratorRequest.php @@ -72,7 +72,7 @@ class UpdateAdministratorRequest implements RequestInterface public ?string $email = null; #[Assert\Type('bool')] - public ?bool $superAdmin = null; + public ?bool $superUser = null; /** * Array of privileges where keys are privilege names (from PrivilegeFlag enum) and values are booleans. @@ -93,7 +93,7 @@ public function getDto(): UpdateAdministratorDto loginName: $this->loginName, password: $this->password, email: $this->email, - superAdmin: $this->superAdmin, + superAdmin: $this->superUser, privileges: $this->privileges ); } diff --git a/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php b/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php index a644a73..68be95a 100644 --- a/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php +++ b/src/Identity/Serializer/AdminAttributeDefinitionNormalizer.php @@ -38,7 +38,6 @@ public function normalize($object, string $format = null, array $context = []): 'list_order' => $object->getListOrder(), 'default_value' => $object->getDefaultValue(), 'required' => $object->isRequired(), - 'table_name' => $object->getTableName(), ]; } diff --git a/src/Identity/Serializer/AdminAttributeValueNormalizer.php b/src/Identity/Serializer/AdminAttributeValueNormalizer.php index d48e74e..d6a0fd7 100644 --- a/src/Identity/Serializer/AdminAttributeValueNormalizer.php +++ b/src/Identity/Serializer/AdminAttributeValueNormalizer.php @@ -12,7 +12,7 @@ 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: 'definition', ref: '#/components/schemas/AdminAttributeDefinition'), new OA\Property(property: 'value', type: 'string', example: 'United States'), ], type: 'object' diff --git a/src/Identity/Serializer/AdministratorNormalizer.php b/src/Identity/Serializer/AdministratorNormalizer.php index fe67b9a..751bfe2 100644 --- a/src/Identity/Serializer/AdministratorNormalizer.php +++ b/src/Identity/Serializer/AdministratorNormalizer.php @@ -17,6 +17,7 @@ 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: 'privileges', type: 'array', items: new OA\Items(type: 'string')), new OA\Property( property: 'created_at', type: 'string', @@ -42,7 +43,7 @@ public function normalize($object, string $format = null, array $context = []): 'id' => $object->getId(), 'login_name' => $object->getLoginName(), 'email' => $object->getEmail(), - 'super_admin' => $object->isSuperUser(), + 'super_user' => $object->isSuperUser(), 'privileges' => $object->getPrivileges()->all(), 'created_at' => $object->getCreatedAt()?->format(DateTimeInterface::ATOM), ]; diff --git a/src/Messaging/Request/Message/MessageScheduleRequest.php b/src/Messaging/Request/Message/MessageScheduleRequest.php index 09af5ca..1108226 100644 --- a/src/Messaging/Request/Message/MessageScheduleRequest.php +++ b/src/Messaging/Request/Message/MessageScheduleRequest.php @@ -13,14 +13,14 @@ required: ['embargo'], properties: [ new OA\Property(property: 'embargo', type: 'string', format: 'date-time', example: '2025-04-17 09:00:00'), - new OA\Property(property: 'repeat_interval', type: 'string', example: '24 hours'), + new OA\Property(property: 'repeat_interval', type: 'integer', example: '24 hours'), new OA\Property( property: 'repeat_until', type: 'string', format: 'date-time', example: '2025-04-30T00:00:00+04:00' ), - new OA\Property(property: 'requeue_interval', type: 'string', example: '12 hours'), + new OA\Property(property: 'requeue_interval', type: 'integer', example: '12 hours'), new OA\Property( property: 'requeue_until', type: 'string', diff --git a/src/Messaging/Serializer/ListMessageNormalizer.php b/src/Messaging/Serializer/ListMessageNormalizer.php index b3e9da1..514ead2 100644 --- a/src/Messaging/Serializer/ListMessageNormalizer.php +++ b/src/Messaging/Serializer/ListMessageNormalizer.php @@ -28,6 +28,7 @@ example: '2022-12-01T10:00:00Z' ), ], + type: 'object' )] class ListMessageNormalizer implements NormalizerInterface { diff --git a/src/Subscription/Serializer/AttributeDefinitionNormalizer.php b/src/Subscription/Serializer/AttributeDefinitionNormalizer.php index ee1e94a..0dae549 100644 --- a/src/Subscription/Serializer/AttributeDefinitionNormalizer.php +++ b/src/Subscription/Serializer/AttributeDefinitionNormalizer.php @@ -13,11 +13,10 @@ properties: [ new OA\Property(property: 'id', type: 'integer', example: 1), new OA\Property(property: 'name', type: 'string', example: 'Country'), - new OA\Property(property: 'type', type: 'string', example: 'checkbox'), + new OA\Property(property: 'type', type: 'string', example: 'checkbox', nullable: true), 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: 'options', type: 'array', diff --git a/src/Subscription/Serializer/SubscriberHistoryNormalizer.php b/src/Subscription/Serializer/SubscriberHistoryNormalizer.php index 7cfe427..2ab5b43 100644 --- a/src/Subscription/Serializer/SubscriberHistoryNormalizer.php +++ b/src/Subscription/Serializer/SubscriberHistoryNormalizer.php @@ -19,7 +19,7 @@ format: 'date-time', example: '2022-12-01T10:00:00Z' ), - new OA\Property(property: 'summery', type: 'string', example: 'Added by admin'), + new OA\Property(property: 'summary', type: 'string', example: 'Added by admin'), new OA\Property(property: 'detail', type: 'string', example: 'Added with add-email on test'), new OA\Property(property: 'system_info', type: 'string', example: 'HTTP_USER_AGENT = Mozilla/5.0'), ], @@ -40,7 +40,7 @@ public function normalize($object, string $format = null, array $context = []): 'id' => $object->getId(), 'ip' => $object->getIp(), 'created_at' => $object->getCreatedAt()->format('Y-m-d\TH:i:sP'), - 'summery' => $object->getSummary(), + 'summary' => $object->getSummary(), 'detail' => $object->getDetail(), 'system_info' => $object->getSystemInfo(), ]; From dd66baca6041148da6d4969d068a39ee4f782bbc Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 19 Nov 2025 22:04:28 +0400 Subject: [PATCH 5/6] Fix tests --- tests/Unit/Identity/Request/UpdateAdministratorRequestTest.php | 2 +- .../Serializer/AdminAttributeDefinitionNormalizerTest.php | 1 - tests/Unit/Identity/Serializer/AdministratorNormalizerTest.php | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/Unit/Identity/Request/UpdateAdministratorRequestTest.php b/tests/Unit/Identity/Request/UpdateAdministratorRequestTest.php index 3eb5228..c8b9c73 100644 --- a/tests/Unit/Identity/Request/UpdateAdministratorRequestTest.php +++ b/tests/Unit/Identity/Request/UpdateAdministratorRequestTest.php @@ -17,7 +17,7 @@ public function testGetDtoReturnsCorrectDto(): void $request->loginName = 'testuser'; $request->password = 'password123'; $request->email = 'test@example.com'; - $request->superAdmin = true; + $request->superUser = true; $request->privileges = [ 'subscribers' => true, 'campaigns' => false, diff --git a/tests/Unit/Identity/Serializer/AdminAttributeDefinitionNormalizerTest.php b/tests/Unit/Identity/Serializer/AdminAttributeDefinitionNormalizerTest.php index f7ad295..8bc4396 100644 --- a/tests/Unit/Identity/Serializer/AdminAttributeDefinitionNormalizerTest.php +++ b/tests/Unit/Identity/Serializer/AdminAttributeDefinitionNormalizerTest.php @@ -32,7 +32,6 @@ public function testNormalizeReturnsExpectedArray(): void 'list_order' => 5, 'default_value' => 'default', 'required' => true, - 'table_name' => 'test_table', ], $data); } diff --git a/tests/Unit/Identity/Serializer/AdministratorNormalizerTest.php b/tests/Unit/Identity/Serializer/AdministratorNormalizerTest.php index 512fa5c..68ee51c 100644 --- a/tests/Unit/Identity/Serializer/AdministratorNormalizerTest.php +++ b/tests/Unit/Identity/Serializer/AdministratorNormalizerTest.php @@ -36,7 +36,7 @@ public function testNormalizeValidAdministrator(): void 'id' => 123, 'login_name' => 'admin', 'email' => 'admin@example.com', - 'super_admin' => true, + 'super_user' => true, 'privileges' => [ 'subscribers' => true, 'campaigns' => false, From 0867d4c9bbcf65951c885aa82a19f41dfd877381 Mon Sep 17 00:00:00 2001 From: Tatevik Date: Wed, 19 Nov 2025 22:12:50 +0400 Subject: [PATCH 6/6] Update: coderabbit configs --- .coderabbit.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index b354963..edaddc7 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -14,3 +14,7 @@ reviews: base_branches: - ".*" drafts: false + +checks: + docstring_coverage: + enabled: false