44
55namespace PhpList \RestBundle \Subscription \Controller ;
66
7+ use DateTimeImmutable ;
8+ use Exception ;
79use OpenApi \Attributes as OA ;
810use PhpList \Core \Domain \Identity \Model \PrivilegeFlag ;
11+ use PhpList \Core \Domain \Subscription \Model \Filter \SubscriberHistoryFilter ;
912use PhpList \Core \Domain \Subscription \Model \Subscriber ;
13+ use PhpList \Core \Domain \Subscription \Model \SubscriberHistory ;
1014use PhpList \Core \Domain \Subscription \Service \Manager \SubscriberManager ;
1115use PhpList \Core \Security \Authentication ;
1216use PhpList \RestBundle \Common \Controller \BaseController ;
17+ use PhpList \RestBundle \Common \Service \Provider \PaginatedDataProvider ;
1318use PhpList \RestBundle \Common \Validator \RequestValidator ;
1419use PhpList \RestBundle \Subscription \Request \CreateSubscriberRequest ;
1520use PhpList \RestBundle \Subscription \Request \UpdateSubscriberRequest ;
1924use Symfony \Component \HttpFoundation \Request ;
2025use Symfony \Component \HttpFoundation \Response ;
2126use Symfony \Component \Routing \Attribute \Route ;
27+ use Symfony \Component \Serializer \Normalizer \NormalizerInterface ;
28+ use Symfony \Component \Validator \Exception \ValidatorException ;
2229
2330/**
2431 * This controller provides REST API access to subscribers.
@@ -31,17 +38,23 @@ class SubscriberController extends BaseController
3138{
3239 private SubscriberManager $ subscriberManager ;
3340 private SubscriberNormalizer $ subscriberNormalizer ;
41+ private PaginatedDataProvider $ paginatedDataProvider ;
42+ private NormalizerInterface $ serializer ;
3443
3544 public function __construct (
3645 Authentication $ authentication ,
3746 RequestValidator $ validator ,
3847 SubscriberManager $ subscriberManager ,
3948 SubscriberNormalizer $ subscriberNormalizer ,
49+ PaginatedDataProvider $ paginatedDataProvider ,
50+ NormalizerInterface $ serializer ,
4051 ) {
4152 parent ::__construct ($ authentication , $ validator );
4253 $ this ->authentication = $ authentication ;
4354 $ this ->subscriberManager = $ subscriberManager ;
4455 $ this ->subscriberNormalizer = $ subscriberNormalizer ;
56+ $ this ->paginatedDataProvider = $ paginatedDataProvider ;
57+ $ this ->serializer = $ serializer ;
4558 }
4659
4760 #[Route('' , name: 'create ' , methods: ['POST ' ])]
@@ -225,6 +238,127 @@ public function getSubscriber(Request $request, int $subscriberId): JsonResponse
225238 return $ this ->json ($ this ->subscriberNormalizer ->normalize ($ subscriber ), Response::HTTP_OK );
226239 }
227240
241+ #[Route('/{subscriberId}/history ' , name: 'history ' , requirements: ['subscriberId ' => '\d+ ' ], methods: ['GET ' ])]
242+ #[OA \Get(
243+ path: '/api/v2/subscribers/{subscriberId}/history ' ,
244+ description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' ,
245+ summary: 'Get subscriber event history ' ,
246+ tags: ['subscribers ' ],
247+ parameters: [
248+ new OA \Parameter (
249+ name: 'php-auth-pw ' ,
250+ description: 'Session key obtained from login ' ,
251+ in: 'header ' ,
252+ required: true ,
253+ schema: new OA \Schema (type: 'string ' )
254+ ),
255+ new OA \Parameter (
256+ name: 'subscriberId ' ,
257+ description: 'Subscriber ID ' ,
258+ in: 'path ' ,
259+ required: true ,
260+ schema: new OA \Schema (type: 'integer ' )
261+ ),
262+ new OA \Parameter (
263+ name: 'after_id ' ,
264+ description: 'Page number (pagination) ' ,
265+ in: 'query ' ,
266+ required: false ,
267+ schema: new OA \Schema (type: 'integer ' , default: 1 )
268+ ),
269+ new OA \Parameter (
270+ name: 'limit ' ,
271+ description: 'Max items per page ' ,
272+ in: 'query ' ,
273+ required: false ,
274+ schema: new OA \Schema (type: 'integer ' , default: 25 )
275+ ),
276+ new OA \Parameter (
277+ name: 'ip ' ,
278+ description: 'Filter by IP address ' ,
279+ in: 'query ' ,
280+ required: false ,
281+ schema: new OA \Schema (type: 'string ' )
282+ ),
283+ new OA \Parameter (
284+ name: 'date_from ' ,
285+ description: 'Filter by date (format: Y-m-d) ' ,
286+ in: 'query ' ,
287+ required: false ,
288+ schema: new OA \Schema (type: 'string ' , format: 'date ' )
289+ ),
290+ new OA \Parameter (
291+ name: 'summery ' ,
292+ description: 'Filter by summary text ' ,
293+ in: 'query ' ,
294+ required: false ,
295+ schema: new OA \Schema (type: 'string ' )
296+ )
297+ ],
298+ responses: [
299+ new OA \Response (
300+ response: 200 ,
301+ description: 'Paginated list of subscriber events ' ,
302+ content: new OA \JsonContent (
303+ properties: [
304+ new OA \Property (
305+ property: 'items ' ,
306+ type: 'array ' ,
307+ items: new OA \Items (ref: '#/components/schemas/SubscriberHistory ' )
308+ ),
309+ new OA \Property (property: 'pagination ' , ref: '#/components/schemas/CursorPagination ' )
310+ ],
311+ type: 'object '
312+ )
313+ ),
314+ new OA \Response (
315+ response: 403 ,
316+ description: 'Unauthorized ' ,
317+ content: new OA \JsonContent (ref: '#/components/schemas/UnauthorizedResponse ' )
318+ ),
319+ new OA \Response (
320+ response: 404 ,
321+ description: 'Not Found ' ,
322+ content: new OA \JsonContent (ref: '#/components/schemas/NotFoundErrorResponse ' )
323+ )
324+ ]
325+ )]
326+ public function getSubscriberHistory (
327+ Request $ request ,
328+ #[MapEntity(mapping: ['subscriberId ' => 'id ' ])] ?Subscriber $ subscriber = null ,
329+ ): JsonResponse {
330+ $ this ->requireAuthentication ($ request );
331+
332+ if (!$ subscriber ) {
333+ throw $ this ->createNotFoundException ('Subscriber not found. ' );
334+ }
335+
336+ try {
337+ $ dateFrom = $ request ->query ->get ('date_from ' );
338+ $ dateFromFormated = $ dateFrom ? new DateTimeImmutable ($ dateFrom ) : null ;
339+ } catch (Exception $ e ) {
340+ throw new ValidatorException ('Invalid date format. Use format: Y-m-d ' );
341+ }
342+
343+ $ filter = new SubscriberHistoryFilter (
344+ subscriber: $ subscriber ,
345+ ip: $ request ->query ->get ('ip ' ),
346+ dateFrom: $ dateFromFormated ,
347+ summery: $ request ->query ->get ('summery ' ),
348+ );
349+
350+ return $ this ->json (
351+ data: $ this ->paginatedDataProvider ->getPaginatedList (
352+ request: $ request ,
353+ normalizer: $ this ->serializer ,
354+ className: SubscriberHistory::class,
355+ filter: $ filter
356+ ),
357+ status: Response::HTTP_OK ,
358+ );
359+ }
360+
361+
228362 #[Route('/{subscriberId} ' , name: 'delete ' , requirements: ['subscriberId ' => '\d+ ' ], methods: ['DELETE ' ])]
229363 #[OA \Delete(
230364 path: '/api/v2/subscribers/{subscriberId} ' ,
0 commit comments