Skip to content

Commit 38332db

Browse files
committed
Add forward email endpoint
1 parent 0cebc96 commit 38332db

22 files changed

+517
-133
lines changed

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@
4242
},
4343
"require": {
4444
"php": "^8.1",
45-
"phplist/core": "dev-main",
45+
"phplist/core": "dev-ref/campaign-processing",
4646
"friendsofsymfony/rest-bundle": "*",
4747
"symfony/test-pack": "^1.0",
4848
"symfony/process": "^6.4",
4949
"zircote/swagger-php": "^4.11",
5050
"ext-dom": "*",
51-
"tatevikgr/rss-feed": "dev-main as 0.1.0"
51+
"tatevikgr/rss-feed": "dev-main as 0.1.0",
52+
"psr/simple-cache": "^3.0"
5253
},
5354
"require-dev": {
5455
"phpunit/phpunit": "^10.0",

config/services/normalizers.yml

Lines changed: 6 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -4,104 +4,16 @@ services:
44
autoconfigure: true
55
public: false
66

7+
_instanceof:
8+
Symfony\Component\Serializer\Normalizer\NormalizerInterface:
9+
tags: [ 'serializer.normalizer' ]
10+
711
Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter: ~
812

913
Symfony\Component\Serializer\Normalizer\ObjectNormalizer:
1014
arguments:
1115
$classMetadataFactory: '@?serializer.mapping.class_metadata_factory'
1216
$nameConverter: '@Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter'
1317

14-
PhpList\RestBundle\Subscription\Serializer\SubscriberNormalizer:
15-
tags: [ 'serializer.normalizer' ]
16-
autowire: true
17-
18-
PhpList\RestBundle\Subscription\Serializer\SubscriberOnlyNormalizer:
19-
tags: [ 'serializer.normalizer' ]
20-
autowire: true
21-
22-
PhpList\RestBundle\Identity\Serializer\AdministratorTokenNormalizer:
23-
tags: [ 'serializer.normalizer' ]
24-
autowire: true
25-
26-
PhpList\RestBundle\Subscription\Serializer\SubscriberListNormalizer:
27-
tags: [ 'serializer.normalizer' ]
28-
autowire: true
29-
30-
PhpList\RestBundle\Subscription\Serializer\SubscriberHistoryNormalizer:
31-
tags: [ 'serializer.normalizer' ]
32-
autowire: true
33-
34-
PhpList\RestBundle\Subscription\Serializer\SubscriptionNormalizer:
35-
tags: [ 'serializer.normalizer' ]
36-
autowire: true
37-
38-
PhpList\RestBundle\Messaging\Serializer\MessageNormalizer:
39-
tags: [ 'serializer.normalizer' ]
40-
autowire: true
41-
42-
PhpList\RestBundle\Messaging\Serializer\TemplateImageNormalizer:
43-
tags: [ 'serializer.normalizer' ]
44-
autowire: true
45-
46-
PhpList\RestBundle\Messaging\Serializer\TemplateNormalizer:
47-
tags: [ 'serializer.normalizer' ]
48-
autowire: true
49-
50-
PhpList\RestBundle\Messaging\Serializer\ListMessageNormalizer:
51-
tags: [ 'serializer.normalizer' ]
52-
autowire: true
53-
54-
PhpList\RestBundle\Identity\Serializer\AdministratorNormalizer:
55-
tags: [ 'serializer.normalizer' ]
56-
autowire: true
57-
58-
PhpList\RestBundle\Identity\Serializer\AdminAttributeDefinitionNormalizer:
59-
tags: [ 'serializer.normalizer' ]
60-
autowire: true
61-
62-
PhpList\RestBundle\Identity\Serializer\AdminAttributeValueNormalizer:
63-
tags: [ 'serializer.normalizer' ]
64-
autowire: true
65-
66-
PhpList\RestBundle\Subscription\Serializer\AttributeDefinitionNormalizer:
67-
tags: [ 'serializer.normalizer' ]
68-
autowire: true
69-
70-
PhpList\RestBundle\Subscription\Serializer\SubscriberAttributeValueNormalizer:
71-
tags: [ 'serializer.normalizer' ]
72-
autowire: true
73-
74-
PhpList\RestBundle\Common\Serializer\CursorPaginationNormalizer:
75-
autowire: true
76-
77-
PhpList\RestBundle\Subscription\Serializer\SubscribersExportRequestNormalizer:
78-
tags: [ 'serializer.normalizer' ]
79-
autowire: true
80-
81-
PhpList\RestBundle\Statistics\Serializer\CampaignStatisticsNormalizer:
82-
tags: [ 'serializer.normalizer' ]
83-
autowire: true
84-
85-
PhpList\RestBundle\Statistics\Serializer\ViewOpensStatisticsNormalizer:
86-
tags: [ 'serializer.normalizer' ]
87-
autowire: true
88-
89-
PhpList\RestBundle\Statistics\Serializer\TopDomainsNormalizer:
90-
tags: [ 'serializer.normalizer' ]
91-
autowire: true
92-
93-
PhpList\RestBundle\Statistics\Serializer\TopLocalPartsNormalizer:
94-
tags: [ 'serializer.normalizer' ]
95-
autowire: true
96-
97-
PhpList\RestBundle\Subscription\Serializer\UserBlacklistNormalizer:
98-
tags: [ 'serializer.normalizer' ]
99-
autowire: true
100-
101-
PhpList\RestBundle\Subscription\Serializer\SubscribePageNormalizer:
102-
tags: [ 'serializer.normalizer' ]
103-
autowire: true
104-
105-
PhpList\RestBundle\Messaging\Serializer\BounceRegexNormalizer:
106-
tags: [ 'serializer.normalizer' ]
107-
autowire: true
18+
PhpList\RestBundle\:
19+
resource: '../../src/*/Serializer/*'

config/services/services.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
services:
2-
PhpList\RestBundle\Subscription\Service\SubscriberService:
2+
PhpList\RestBundle\Subscription\Service\SubscriberHistoryService:
33
autowire: true
44
autoconfigure: true
55

6-
PhpList\RestBundle\Subscription\Service\SubscriberHistoryService:
6+
PhpList\Core\Domain\Messaging\Service\ForwardingGuard:
7+
autowire: true
8+
autoconfigure: true
9+
public: false
10+
11+
PhpList\Core\Domain\Messaging\Service\ForwardDeliveryService:
12+
autowire: true
13+
autoconfigure: true
14+
public: false
15+
16+
PhpList\Core\Domain\Messaging\Service\ForwardContentService:
717
autowire: true
818
autoconfigure: true
19+
public: false

src/Identity/Controller/AdminAttributeDefinitionController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use Doctrine\ORM\EntityManagerInterface;
88
use OpenApi\Attributes as OA;
99
use PhpList\Core\Domain\Identity\Model\AdminAttributeDefinition;
10-
use PhpList\Core\Domain\Identity\Service\AdminAttributeDefinitionManager;
10+
use PhpList\Core\Domain\Identity\Service\Manager\AdminAttributeDefinitionManager;
1111
use PhpList\Core\Security\Authentication;
1212
use PhpList\RestBundle\Common\Controller\BaseController;
1313
use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider;

src/Identity/Controller/AdminAttributeValueController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
use PhpList\Core\Domain\Identity\Model\Administrator;
1111
use PhpList\Core\Domain\Identity\Model\AdminAttributeDefinition;
1212
use PhpList\Core\Domain\Identity\Model\AdminAttributeValue;
13-
use PhpList\Core\Domain\Identity\Service\AdminAttributeManager;
13+
use PhpList\Core\Domain\Identity\Service\Manager\AdminAttributeManager;
1414
use PhpList\Core\Security\Authentication;
1515
use PhpList\RestBundle\Common\Controller\BaseController;
1616
use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider;

src/Identity/Controller/AdministratorController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use Doctrine\ORM\EntityManagerInterface;
88
use OpenApi\Attributes as OA;
99
use PhpList\Core\Domain\Identity\Model\Administrator;
10-
use PhpList\Core\Domain\Identity\Service\AdministratorManager;
10+
use PhpList\Core\Domain\Identity\Service\Manager\AdministratorManager;
1111
use PhpList\Core\Security\Authentication;
1212
use PhpList\RestBundle\Common\Controller\BaseController;
1313
use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider;

src/Identity/Controller/PasswordResetController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use Doctrine\ORM\EntityManagerInterface;
88
use OpenApi\Attributes as OA;
9-
use PhpList\Core\Domain\Identity\Service\PasswordManager;
9+
use PhpList\Core\Domain\Identity\Service\Manager\PasswordManager;
1010
use PhpList\Core\Security\Authentication;
1111
use PhpList\RestBundle\Common\Controller\BaseController;
1212
use PhpList\RestBundle\Common\Validator\RequestValidator;

src/Identity/Controller/SessionController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use Doctrine\ORM\EntityManagerInterface;
88
use OpenApi\Attributes as OA;
99
use PhpList\Core\Domain\Identity\Model\AdministratorToken;
10-
use PhpList\Core\Domain\Identity\Service\SessionManager;
10+
use PhpList\Core\Domain\Identity\Service\Manager\SessionManager;
1111
use PhpList\Core\Security\Authentication;
1212
use PhpList\RestBundle\Common\Controller\BaseController;
1313
use PhpList\RestBundle\Common\Validator\RequestValidator;

src/Messaging/Controller/CampaignController.php

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,14 @@
2929
#[Route('/campaigns', name: 'campaign_')]
3030
class CampaignController extends BaseController
3131
{
32-
private CampaignService $campaignService;
33-
private MessageBusInterface $messageBus;
34-
3532
public function __construct(
3633
Authentication $authentication,
3734
RequestValidator $validator,
38-
CampaignService $campaignService,
39-
MessageBusInterface $messageBus,
35+
private readonly CampaignService $campaignService,
36+
private readonly MessageBusInterface $messageBus,
4037
private readonly EntityManagerInterface $entityManager,
4138
) {
4239
parent::__construct($authentication, $validator);
43-
$this->campaignService = $campaignService;
44-
$this->messageBus = $messageBus;
4540
}
4641

4742
#[Route('', name: 'get_list', methods: ['GET'])]
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpList\RestBundle\Messaging\Controller;
6+
7+
use Doctrine\ORM\EntityManagerInterface;
8+
use OpenApi\Attributes as OA;
9+
use PhpList\Core\Domain\Messaging\Model\Dto\MessageForwardDto;
10+
use PhpList\Core\Domain\Messaging\Model\Message;
11+
use PhpList\Core\Domain\Messaging\Service\MessageForwardService;
12+
use PhpList\Core\Security\Authentication;
13+
use PhpList\RestBundle\Common\Controller\BaseController;
14+
use PhpList\RestBundle\Common\Validator\RequestValidator;
15+
use PhpList\RestBundle\Messaging\Request\ForwardMessageRequest;
16+
use PhpList\RestBundle\Messaging\Serializer\ForwardingResultNormalizer;
17+
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
18+
use Symfony\Component\HttpFoundation\JsonResponse;
19+
use Symfony\Component\HttpFoundation\Request;
20+
use Symfony\Component\HttpFoundation\Response;
21+
use Symfony\Component\Routing\Attribute\Route;
22+
23+
/**
24+
* This controller provides REST API for email forwarding
25+
*
26+
* @author Tatevik Grigoryan <tatevik@phplist.com>
27+
*/
28+
#[Route('/email-forward', name: 'email_forward_')]
29+
class EmailForwardController extends BaseController
30+
{
31+
public function __construct(
32+
Authentication $authentication,
33+
RequestValidator $validator,
34+
private readonly EntityManagerInterface $entityManager,
35+
private readonly MessageForwardService $messageForwardService,
36+
private readonly ForwardingResultNormalizer $forwardingResultNormalizer,
37+
) {
38+
parent::__construct($authentication, $validator);
39+
}
40+
41+
#[Route('/{messageId}', name: 'forward', requirements: ['messageId' => '\\d+'], methods: ['POST'])]
42+
#[OA\Post(
43+
path: '/api/v2/campaigns/{messageId}/forward',
44+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
45+
'Queues forwarding of a campaign/message to provided recipient emails.',
46+
summary: 'Forward a message to recipients.',
47+
requestBody: new OA\RequestBody(
48+
description: 'Forwarding payload',
49+
required: true,
50+
content: new OA\JsonContent(ref: '#/components/schemas/ForwardMessageRequest')
51+
),
52+
tags: ['campaigns'],
53+
parameters: [
54+
new OA\Parameter(
55+
name: 'php-auth-pw',
56+
description: 'Session key obtained from login',
57+
in: 'header',
58+
required: true,
59+
schema: new OA\Schema(type: 'string')
60+
),
61+
new OA\Parameter(
62+
name: 'messageId',
63+
description: 'message ID',
64+
in: 'path',
65+
required: true,
66+
schema: new OA\Schema(type: 'string')
67+
)
68+
],
69+
responses: [
70+
new OA\Response(
71+
response: 202,
72+
description: 'Accepted',
73+
content: new OA\JsonContent(ref: '#/components/schemas/ForwardResult')
74+
),
75+
new OA\Response(
76+
response: 403,
77+
description: 'Failure',
78+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
79+
),
80+
new OA\Response(
81+
response: 404,
82+
description: 'Failure',
83+
content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse')
84+
),
85+
new OA\Response(
86+
response: 422,
87+
description: 'Failure',
88+
content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse')
89+
)
90+
]
91+
)]
92+
public function forwardMessage(
93+
Request $request,
94+
#[MapEntity(mapping: ['messageId' => 'id'])] ?Message $message = null
95+
): JsonResponse {
96+
if ($message === null) {
97+
throw $this->createNotFoundException('Campaign not found.');
98+
}
99+
100+
/** @var ForwardMessageRequest $forwardRequest */
101+
$forwardRequest = $this->validator->validate($request, ForwardMessageRequest::class);
102+
103+
$result = $this->messageForwardService->forward(
104+
messageForwardDto: new MessageForwardDto(
105+
emails: $forwardRequest->recipients,
106+
uid: $forwardRequest->uid,
107+
fromName: $forwardRequest->fromName,
108+
fromEmail: $forwardRequest->fromEmail,
109+
note: $forwardRequest->note,
110+
),
111+
campaign: $message,
112+
);
113+
114+
$this->entityManager->flush();
115+
116+
return $this->json(
117+
$this->forwardingResultNormalizer->normalize($result),
118+
Response::HTTP_ACCEPTED
119+
);
120+
}
121+
}

0 commit comments

Comments
 (0)