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