Skip to content

Commit 97d5bbd

Browse files
committed
Add dashboard_statistics
1 parent 8801ad9 commit 97d5bbd

3 files changed

Lines changed: 215 additions & 0 deletions

File tree

src/Statistics/Controller/AnalyticsController.php

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\HttpFoundation\Request;
1919
use Symfony\Component\HttpFoundation\Response;
2020
use Symfony\Component\Routing\Attribute\Route;
21+
use Throwable;
2122

2223
/**
2324
* This controller provides REST API to access analytics data.
@@ -356,4 +357,101 @@ public function getTopLocalParts(Request $request): JsonResponse
356357

357358
return $this->json($normalizedData, Response::HTTP_OK);
358359
}
360+
361+
#[Route('/dashboard', name: 'dashboard_statistics', methods: ['GET'])]
362+
#[OA\Get(
363+
path: '/api/v2/analytics/dashboard',
364+
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
365+
'Returns dashboard cards with aggregate analytics metrics.',
366+
summary: 'Gets dashboard analytics statistics.',
367+
tags: ['analytics'],
368+
parameters: [
369+
new OA\Parameter(
370+
name: 'php-auth-pw',
371+
description: 'Session key obtained from login',
372+
in: 'header',
373+
required: true,
374+
schema: new OA\Schema(type: 'string')
375+
)
376+
],
377+
responses: [
378+
new OA\Response(
379+
response: 200,
380+
description: 'Success',
381+
content: new OA\JsonContent(
382+
properties: [
383+
new OA\Property(
384+
property: 'total_subscribers',
385+
properties: [
386+
new OA\Property(property: 'value', type: 'integer', example: 48294),
387+
new OA\Property(
388+
property: 'change_vs_last_month',
389+
type: 'number',
390+
format: 'float',
391+
example: 12.5
392+
),
393+
],
394+
type: 'object'
395+
),
396+
new OA\Property(
397+
property: 'active_campaigns',
398+
properties: [
399+
new OA\Property(property: 'value', type: 'integer', example: 12),
400+
new OA\Property(
401+
property: 'change_vs_last_month',
402+
type: 'number',
403+
format: 'float',
404+
example: 0
405+
),
406+
],
407+
type: 'object'
408+
),
409+
new OA\Property(
410+
property: 'open_rate',
411+
properties: [
412+
new OA\Property(property: 'value', type: 'number', format: 'float', example: 12),
413+
new OA\Property(
414+
property: 'change_vs_last_month',
415+
type: 'number',
416+
format: 'float',
417+
example: 0
418+
),
419+
],
420+
type: 'object'
421+
),
422+
new OA\Property(
423+
property: 'bounce_rate',
424+
properties: [
425+
new OA\Property(property: 'value', type: 'number', format: 'float', example: 12),
426+
new OA\Property(
427+
property: 'change_vs_last_month',
428+
type: 'number',
429+
format: 'float',
430+
example: 0
431+
),
432+
],
433+
type: 'object'
434+
),
435+
],
436+
type: 'object'
437+
)
438+
),
439+
new OA\Response(
440+
response: 403,
441+
description: 'Unauthorized',
442+
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
443+
)
444+
]
445+
)]
446+
public function getDashboardStatistics(Request $request): JsonResponse
447+
{
448+
$authUser = $this->requireAuthentication($request);
449+
if (!$authUser->getPrivileges()->has(PrivilegeFlag::Statistics)) {
450+
throw $this->createAccessDeniedException('You are not allowed to access statistics.');
451+
}
452+
453+
$response = $this->analyticsService->getSummaryStatistics();
454+
455+
return $this->json($response, Response::HTTP_OK);
456+
}
359457
}

tests/Integration/Statistics/Controller/AnalyticsControllerTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,38 @@ public function testGetTopLocalPartsWithInvalidLimitParameter(): void
254254
self::assertArrayHasKey('local_parts', $response);
255255
self::assertIsArray($response['local_parts']);
256256
}
257+
258+
public function testGetDashboardStatisticsWithoutSessionKeyReturnsForbidden(): void
259+
{
260+
self::getClient()->request('GET', '/api/v2/analytics/dashboard');
261+
$this->assertHttpForbidden();
262+
}
263+
264+
public function testGetDashboardStatisticsWithValidSessionReturnsCardsData(): void
265+
{
266+
$this->loadFixtures([
267+
AdministratorFixture::class,
268+
AdministratorTokenFixture::class,
269+
SubscriberFixture::class,
270+
MessageFixture::class,
271+
]);
272+
273+
$this->authenticatedJsonRequest('GET', '/api/v2/analytics/dashboard');
274+
$this->assertHttpOkay();
275+
$response = $this->getDecodedJsonResponseContent();
276+
277+
self::assertIsArray($response);
278+
self::assertArrayHasKey('total_subscribers', $response);
279+
self::assertArrayHasKey('active_campaigns', $response);
280+
self::assertArrayHasKey('open_rate', $response);
281+
self::assertArrayHasKey('bounce_rate', $response);
282+
283+
foreach (['total_subscribers', 'active_campaigns', 'open_rate', 'bounce_rate'] as $metric) {
284+
self::assertIsArray($response[$metric]);
285+
self::assertArrayHasKey('value', $response[$metric]);
286+
self::assertArrayHasKey('change_vs_last_month', $response[$metric]);
287+
self::assertIsNumeric($response[$metric]['value']);
288+
self::assertIsNumeric($response[$metric]['change_vs_last_month']);
289+
}
290+
}
257291
}

tests/Unit/Statistics/Controller/AnalyticsControllerTest.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,4 +442,87 @@ public function testGetTopLocalPartsReturnsJsonResponse(): void
442442
'total' => 1,
443443
], json_decode($response->getContent(), true));
444444
}
445+
446+
public function testGetDashboardStatisticsWithoutStatisticsPrivilegeThrowsException(): void
447+
{
448+
$request = new Request();
449+
450+
$this->authentication
451+
->expects(self::once())
452+
->method('authenticateByApiKey')
453+
->with($request)
454+
->willReturn($this->administrator);
455+
456+
$this->privileges
457+
->expects(self::once())
458+
->method('has')
459+
->with(PrivilegeFlag::Statistics)
460+
->willReturn(false);
461+
462+
$this->expectException(AccessDeniedException::class);
463+
$this->expectExceptionMessage('You are not allowed to access statistics.');
464+
465+
$this->controller->getDashboardStatistics($request);
466+
}
467+
468+
public function testGetDashboardStatisticsReturnsJsonResponse(): void
469+
{
470+
$request = new Request();
471+
472+
$this->authentication
473+
->expects(self::once())
474+
->method('authenticateByApiKey')
475+
->with($request)
476+
->willReturn($this->administrator);
477+
478+
$this->privileges
479+
->expects(self::once())
480+
->method('has')
481+
->with(PrivilegeFlag::Statistics)
482+
->willReturn(true);
483+
484+
$this->analyticsService
485+
->expects(self::once())
486+
->method('getSummaryStatistics')
487+
->willReturn([
488+
'total_subscribers' => [
489+
'value' => 80,
490+
'change_vs_last_month' => 10.5,
491+
],
492+
'active_campaigns' => [
493+
'value' => 12,
494+
'change_vs_last_month' => -4.25,
495+
],
496+
'open_rate' => [
497+
'value' => 40.0,
498+
'change_vs_last_month' => 3.3,
499+
],
500+
'bounce_rate' => [
501+
'value' => 6.67,
502+
'change_vs_last_month' => -1.1,
503+
],
504+
]);
505+
506+
$response = $this->controller->getDashboardStatistics($request);
507+
508+
self::assertEquals(Response::HTTP_OK, $response->getStatusCode());
509+
self::assertEquals([
510+
'total_subscribers' => [
511+
'value' => 80,
512+
'change_vs_last_month' => 10.5,
513+
],
514+
'active_campaigns' => [
515+
'value' => 12,
516+
'change_vs_last_month' => -4.25,
517+
],
518+
'open_rate' => [
519+
'value' => 40.0,
520+
'change_vs_last_month' => 3.3,
521+
],
522+
'bounce_rate' => [
523+
'value' => 6.67,
524+
'change_vs_last_month' => -1.1,
525+
],
526+
], json_decode($response->getContent(), true));
527+
}
445528
}

0 commit comments

Comments
 (0)